Curso R: Clase 1
Curso R: Clase 1

Visión General de una base

Cargamos bibliotecas (library)

#![Texto Alternativo](URL_de_la_imagen)
#![**Curso R: Clase 1**](logoaustral.png){width=width='100px' height=width='100px'}

library(readxl) #lectura de archivos en Excel
library(readr) # para leer datos de texto rectangulares
library(dplyr) # manejo de datos
library(tidyverse) # manejo de datos
library(DT)
library(kableExtra) # hacer tablas bonitas
library(lubridate) # manejo de fechas
library(nycflights13) # archivo de datos de vuelos
library(googlesheets4) # para leer desde el Google Drive
library(foreign)
library(utf8)

Los Chunks

En los Chunks nos puede interesar configurar que no nos envie mensajes o que no muestre el código o bien que no muestre la ejecución de un código. A continuación cómo hacerlo:

  • cache=FALSE, message=FALSE, warning=FALSE, sirven para que los comandos que mandan mensajes no los impriman en pantalla. Esto es importante si queremos que nuestros lectores no sepan lo que está corriendo por detrás.

  • eval = FALSE, sirve para que los códigos se muestren, pero que no se ejecuten. Esto es útil cuando escribimos tutoriales de R pero no queremos que se ejecute lo que hay dentro.

  • echo = FALSE, sirve para que los códigos se ejecuten pero no se muestren. Esto es útil cuando estamos escribiendo un artículo y queremos que nuestro código se ejecute (por ejemplo, para crear una gráfica o un mapa) pero no queremos que los lectores vean las tripas, o el código que lo genera.

Leamos un archivo xlsx desde una ruta y lo visualizamos

baseIMC <- read_excel("../../IMCinfantil.xlsx")


baseIMC

cómo podemos acceder a una columna (variable) - modo clásico

baseIMC[,4] # accedemos a la cuarta columna

baseIMC$PESO # accedemos a la misma columna pero por su nombre
##   [1] 24.4 23.6 47.0 24.0 23.9 41.0 32.9 22.4 28.7 31.4 28.9 51.2 26.2 58.5 23.7
##  [16] 25.5 49.7 39.6 42.5 21.6 38.0 26.6 20.4 23.7 21.4 45.7 51.3 28.0 26.9 43.9
##  [31] 25.7 28.2 36.8 27.3 33.8 23.5 33.1 37.5 33.2 36.8 29.2 30.6 28.1 23.5 19.8
##  [46] 23.6 26.0 37.6 25.5 40.0 80.8 30.0 44.9 51.6 27.0 40.0 32.3 24.5 48.5 43.5
##  [61] 42.6 38.0 23.9 52.0 55.2 48.0 50.5 19.0 32.0 33.5 38.0 24.5 31.5 21.2 19.9
##  [76] 26.7 23.0 46.3 38.2 21.2 24.2 24.3 30.0 37.3 49.4 24.7 35.0 22.3 35.5 38.4
##  [91] 32.2 40.3 40.5 59.5 43.0 20.3 22.0 58.0 37.5 41.3 18.5 19.1 32.5 34.6 29.6
## [106] 33.8 42.3 31.0 23.4 21.5 21.3 39.5 31.5 30.3 22.3 60.2 23.6 22.6 27.5 34.2
## [121] 46.5 54.8 22.6 30.3 19.7 35.4 20.8 40.2 57.3 45.5 28.4 34.6 26.4 35.0 23.7
## [136] 26.4 52.4 32.6 41.1 23.3 38.2 25.5 33.1 21.6 32.9 52.9 26.5 21.5 21.5 23.6

cómo podemos acceder a una fila (registro)- modo clásico

baseIMC[3,]
baseIMC[baseIMC$PACIENTE=="3",]

cómo podemos acceder a un dato (fila y columna especificas)

baseIMC[3,2]
baseIMC[baseIMC$PACIENTE=="3",2]

Cuál es la diferencia entre ambas salidas?

Leemos datos desde una url

url.dat <- "http://bit.ly/Database-Estudiantes"    # La url desde drive
estudiantes <- read.delim(url.dat) 
estudiantes

Ahora una forma más moderna: La Biblioteca dplyr

El paquete dplyr proporciona una forma bastante ágil de manejar los ficheros de datos de R. Además, el código escrito usando este paquete (especialmente usando la sintaxis en cadena que veremos más adelante) es mucho más legible y fácil de entender.

El paquete incluye un conjunto de comandos que coinciden con las acciones más comunes que se realizan sobre un conjunto de datos (seleccionar filas filter, seleccionar columnas select, ordenar arrange, añadir nuevas variables mutate, resumir mediante alguna medida numérica summarise). Lo que hace que la sintaxis sea especialmente clara es la correspondencia tan nítida entre el comando y la acción. Para llevar a cabo estas acciones debemos tener en cuenta algunas características comunes:

  • El primer argumento siempre es un data.frame
  • El resto de argumentos indican lo que queremos hacer con el data.frame.
  • El resultado siempre tiene también la estructura de data.frame

Seleccionar filas: filter()

Esta acción consiste en seleccionar las observaciones (filas) que cumplen las condiciones que nos interesan.**

Tres ejemplos

  • el primer comando selecciona todos los estudiantes categorizados como altos.
  • el segundo selecciona los estudiantes categorizados como altos o medios.
  • el tercero selecciona los estudiantes altos menores a 17 años.

filter(estudiantes, Estatura=='Alta')
filter(estudiantes, Estatura=='Alta' | Estatura=='Media')
filter(estudiantes, Estatura=='Alta', Edad < 17)

Seleccionar columnas: select()

Esta acción consiste en elegir un subconjunto de las variables (columnas) del fichero. Por ejemplo podemos seleccionar de la base de estudiantes las variables Estatura y el Colegio.

select(estudiantes, Estatura, Colegio)

Dos ejemplos

  • Es posible seleccionar un rango de variables utilizando “:”
  • o elegir todas las variables menos algunas.
  • El primer ejemplo selecciona todas las variables entre Colegio y P3
  • El segundo selecciona todas menos Sexo
  • El tercero excluye las variables desde Gastos hasta AGPEQ2 inclusive
select(estudiantes, Colegio:P3)
select(estudiantes, -Sexo)
select(estudiantes, -(Gastos:AGPEQ2))

Otra posibilidad es seleccionar las variables cuyo nombre contenga ciertos términos:

  • El primer ejemplo selecciona las variables que en su nombre contienen la palabra Sexo.
  • El segundo ejemplo a las mismas variables las excluye.
select(estudiantes, contains('Sexo')) 
select(estudiantes, -contains('Sexo')) 
select(estudiantes,starts_with('p'))
select(estudiantes,ends_with('s'))

Ordenar: arrange()

Ordena las filas de menor a mayor valor de la variable elegida.

Si escribimos un signo menos, ordena de mayor a menor.

Por ejemplo para ordenar de la base de estudiantes en función de la Edad:

  • El primer ejemplo ordena la base de estudiantes en función de la Edad de menor a mayor
  • El segundo ejemplo lo hace de mayor a menor
  • El tercer ejemplo ordena primero por Edad y segundo por la nota de la evaluación Final
  • El cuarto ejemplo lo ordena en forma creciente de edad y decreciente de nota de evaluación final
   
arrange(estudiantes, Edad) 
arrange(estudiantes, -Edad)
arrange(estudiantes, Edad, Final)
arrange(estudiantes, Edad, -Final)

Sintaxis en cadena

El paquete incorpora una sintaxis encadenada que permite escribir las acciones en un orden natural, en oposición a la forma anidada en la que lo haríamos normalmente.

Primero se escribe el nombre del fichero y luego las acciones

En el orden en que se realizan separadas por el operador %>% (que podríamos leer como entonces ). Este operador se denomina “pipe” y se traduce como caño. Por ejemplo, si queremos seleccionar las variables que contienen las medidas del pétalo, seleccionar los lirios para los que la longitud del pétalo es mayor que 4 mm y ordenarlos de menor a mayor longitud del pétalo, podemos escribir:



estudiantes %>%
  select(contains('P'))%>%
  select(-contains('A')) %>%
  filter(P1 > 4)%>%
  arrange(P2)

Para seleccionar las variables numéricas solamente

estudiantes %>% 
  select_if(., is.numeric)

Para seleccionar las variables que son de tipo caracter solamente

estudiantes %>% 
  select_if(., is.character)

Podemos seleccionar algunos registros con slice()

baseIMC %>% 
  slice(1, 11:13, 21)

#- Nos quedamos aca

Ejercicio 1

Explica con tus palabras la secuencia anterior.

Respuesta: slice(1, 11:13, 21): selecciona las filas 1, del 11 al 13 y la 21.

Ejercicio 2

Con la base de IMC que vimos al principio.

  1. Seleccionar las variables Peso, Talla e IMC.
select(baseIMC, PESO, TALLA, IMC)
  1. Seleccionar los registros de las mujeres.
filter(baseIMC, SEXO=='F')
  1. De los registros de las mujeres, seleccionar las variables Edad y CatPeso.
baseIMC %>%
  filter(SEXO == 'F')%>%
  select(SEXO,EDAD,CatPeso)
  1. De los registros de las mujeres, seleccionar las variables Edad y Catpeso y luego ordenarlos en forma creciente por Edad.
baseIMC %>% 
  filter(SEXO == 'F') %>% 
  select(SEXO,EDAD,CatPeso) %>% 
  arrange(EDAD)
  1. Ordenar la base en forma decreciente por IMC.
baseIMC %>% 
  arrange(-IMC)
  1. De la base ordenada en forma decreciente por IMC, seleccionar los que tienen sobrepeso y quedarse con las columnas de Edad, Talla y Peso.
baseIMC %>% 
  arrange(-IMC) %>% 
  filter(IMC > 30) %>% 
  select(IMC, EDAD, TALLA, PESO)
  1. Seleccionar las variables Talla, IMC y CC y filtrar los registros 1, 5, 6, 7 y 15. Guardar esto en una base que se llame baseIMC_aux y mostrar cómo es la nueva base.
baseIMC_aux <- baseIMC %>% 
  select(TALLA, IMC, CC) %>%
  slice(1,5:6,5)

baseIMC_aux

Añadir nuevas variables: mutate()

Seguimos con las acciones básicas implementadas en la biblioteca dplyr.

Veamos como crear nuevas variables que son función de las ya existentes.

En este ejemplo creamos una variable que corresponde al cociente entre la Talla y la CC (que podría corresponder a algún aspecto de la forma de la persona de interés para su riesgo cardiovascular) en la baseIMC:

baseIMC<-baseIMC %>%
  mutate(forma = TALLA/CC)
baseIMC

Observemos que la nueva variable forma aparece en la última columna.

Podríamos redondear los valores de esta nueva variable, por ejemplo con tres cifras decimales.

baseIMC<-baseIMC %>%
  mutate(forma = round(TALLA/CC,3))
baseIMC

Resumir (subconjuntos de) variables: group_by() + summarise()

Usamos summarise() para aplicar comandos a variables.

Normalmente se usa en combinación con group_by() de manera que se calculen estadísticos para subgrupos de observaciones.

En el siguiente ejemplo se calcula la media del IMC para cada categoría de Peso:

resumen1<-baseIMC %>%
    group_by(CatPeso) %>% summarise(Media_forma=mean(forma),n=n(),stdev=sd(forma)) %>% mutate_if(is.numeric, ~round(., 4))
resumen1

Para extraer aleatoriamente algunas observaciones sample_n()

Extrae 4 obs. sin reemplazo de la base de estudiantes

# Podemos fijar una semilla que nos hace repetible al experimento
set.seed(27)
estudiantes %>% sample_n(4)

Extrae un 25% de obs con/sin reemplazamiento

set.seed(123)
estudiantes %>% sample_frac(0.25, rep=FALSE) %>% arrange(Observacion) 
# en este caso podemos optar por con o sin reposición en el muestreo

Hay distinas posibilidades que nos permiten visualizar las variables su tipo y algunos valores, veamos similitudes y diferencias

 glimpse(baseIMC)
## Rows: 150
## Columns: 10
## $ PACIENTE <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18…
## $ EDAD     <dbl> 7, 7, 8, 7, 7, 10, 7, 7, 7, 9, 9, 11, 7, 9, 9, 11, 12, 7, 11,…
## $ SEXO     <chr> "M", "M", "M", "F", "M", "M", "M", "M", "M", "M", "M", "F", "…
## $ PESO     <dbl> 24.4, 23.6, 47.0, 24.0, 23.9, 41.0, 32.9, 22.4, 28.7, 31.4, 2…
## $ TALLA    <dbl> 1.2, 1.2, 1.4, 1.2, 1.2, 1.4, 1.3, 1.2, 1.3, 1.3, 1.3, 1.6, 1…
## $ IMC      <dbl> 16.94444, 16.38889, 23.97959, 16.66667, 16.59722, 20.91837, 1…
## $ PIMC     <dbl> 7.97, 72.72, 97.08, 83.88, 45.85, 87.33, 96.57, 32.88, 80.77,…
## $ CC       <dbl> 54, 52, 76, 63, 56, 78, 69, 52, 60, 69, 60, 75, 50, 88, 58, 7…
## $ CatPeso  <chr> "N", "N", "OB", "N", "N", "SO", "OB", "N", "N", "SO", "N", "N…
## $ forma    <dbl> 0.022, 0.023, 0.018, 0.019, 0.021, 0.018, 0.019, 0.023, 0.022…
summary(baseIMC)
##     PACIENTE           EDAD            SEXO                PESO      
##  Min.   :  1.00   Min.   : 6.000   Length:150         Min.   :18.50  
##  1st Qu.: 38.25   1st Qu.: 7.000   Class :character   1st Qu.:23.93  
##  Median : 75.50   Median : 8.000   Mode  :character   Median :31.45  
##  Mean   : 75.50   Mean   : 8.327                      Mean   :33.40  
##  3rd Qu.:112.75   3rd Qu.: 9.000                      3rd Qu.:40.00  
##  Max.   :150.00   Max.   :12.000                      Max.   :80.80  
##      TALLA            IMC             PIMC             CC        
##  Min.   :1.100   Min.   :13.26   Min.   : 2.02   Min.   : 50.00  
##  1st Qu.:1.200   1st Qu.:16.27   1st Qu.:53.02   1st Qu.: 56.25  
##  Median :1.300   Median :17.77   Median :79.75   Median : 63.00  
##  Mean   :1.322   Mean   :18.60   Mean   :68.56   Mean   : 65.25  
##  3rd Qu.:1.400   3rd Qu.:20.86   3rd Qu.:91.23   3rd Qu.: 72.75  
##  Max.   :1.700   Max.   :31.56   Max.   :99.27   Max.   :107.00  
##    CatPeso              forma        
##  Length:150         Min.   :0.01500  
##  Class :character   1st Qu.:0.01900  
##  Mode  :character   Median :0.02100  
##                     Mean   :0.02056  
##                     3rd Qu.:0.02200  
##                     Max.   :0.02600
str(baseIMC)
## tibble [150 × 10] (S3: tbl_df/tbl/data.frame)
##  $ PACIENTE: num [1:150] 1 2 3 4 5 6 7 8 9 10 ...
##  $ EDAD    : num [1:150] 7 7 8 7 7 10 7 7 7 9 ...
##  $ SEXO    : chr [1:150] "M" "M" "M" "F" ...
##  $ PESO    : num [1:150] 24.4 23.6 47 24 23.9 41 32.9 22.4 28.7 31.4 ...
##  $ TALLA   : num [1:150] 1.2 1.2 1.4 1.2 1.2 1.4 1.3 1.2 1.3 1.3 ...
##  $ IMC     : num [1:150] 16.9 16.4 24 16.7 16.6 ...
##  $ PIMC    : num [1:150] 7.97 72.72 97.08 83.88 45.85 ...
##  $ CC      : num [1:150] 54 52 76 63 56 78 69 52 60 69 ...
##  $ CatPeso : chr [1:150] "N" "N" "OB" "N" ...
##  $ forma   : num [1:150] 0.022 0.023 0.018 0.019 0.021 0.018 0.019 0.023 0.022 0.019 ...

Otros resúmenes como desvío standard y cantidad de registros

baseIMC %>% 
  select(TALLA,SEXO) %>% 
  group_by(SEXO) %>% 
  summarise(Total_Registros=n(), 
    media_talla=mean(TALLA), 
    sd_talla=sd(TALLA)) %>% 
  arrange(-media_talla)

Para que muestre algunos registros de los primeros usamos head() Para que muestre algunos registros de los últimos usamos tail().

estudiantes %>% arrange(desc(Final)) %>% head(8)
estudiantes %>% arrange(Final) %>% tail(8)

Para agregar una nueva fila a la base de datos podemos utilizar add_row():

baseIMC1 <- baseIMC %>% add_row(PACIENTE= 151, EDAD = 12, SEXO="M",  TALLA=1.3,IMC=16.7,CC=75)
baseIMC1

Observen qué ocurrió con las variables que no cargamos!!

Otra característica importante es el conteo, podríamos estar interesados, dado un conjunto de clases cual es su frecuencia absoluta de cada categoría, para esto utilizaremos count() el cual genera una variable que contiene dichas frecuencias.

Ahora eliminamos la/s fila/s que tengan algún registro faltante

baseIMC1 %>% na.omit()
baseIMC %>% count(CatPeso)


#table(baseIMC$CatPeso)
baseIMC%>%summarize(n_distinct(CatPeso))
#length(unique(baseIMC$CatPeso))

Podemos utilizar el condicional ifelse() para crear nuevas variables según ciertas condiciones:

baseIMC2<-baseIMC %>% mutate(categoría = ifelse(EDAD > 10, "Mayor", "Menor"))
baseIMC2

Renombrando variables

También podemos cambiar el nombre a una variable.

estudiantes %>% 
  rename(Gasto_total = Gastos)


#- Tambien tenemos estas opciones
#names(df)[names(df) == 'old.var.name'] <- 'new.var.name'
#colnames(df)[position] <- "newname2"

Cuando quiero cambiar el nombre de todas las columnas setNames()

baseIMC3<-baseIMC %>% 
  setNames(c("Paciente", "Edad", "Sexo","Peso","Talla","BMI","BMI_perc","PC","Cat_Peso","Forma"))
baseIMC3

Ejercicio 3

Con los datos de Admisión, cuyas variables indican si el estudiante fue admitido (categoría 1) en una carrera de posgrado, el puntaje del estudiantes en GRE (graduate record exam scores-puntajes de exámenes de registro de posgrado) y GPA (grade point average- promedio de calificaciones de la carrera de grado) y el rank(rank of the undergraduate school- rango de la escuela de pregrado).

admision <- read.dta(url)  
head(admision,8)
admision
  1. Crear una nueva variable que promedie los valores de gre y gpa.
  2. Agrupar por rank y promediar gre y gpa para cada rank.
  3. Extraer una muestra de 100 registros sin reposición de dos maneras distintas.
  4. Resumir las variables de la base
  5. Agregar una fila que resulte muy diferente a las otras y no colocar rank.
  6. Eliminar los registros que tengan algún valor faltante.
  7. Contar cuántos fueron admitidos y cuantos no.
  8. Contar cuántos hay de cada rango de escuela.
  9. Cambiar el nombre de una las variables (sean creativos!).
  10. Cambiar el nombre de todas las variables (pónganle humor!).

Cambiar niveles de un factor

En la base de estudiantes queremos recodificar la variable Fuma

df <- estudiantes[1:40,1:10]
df<- df %>% mutate_at("Fuma", factor)
levels(df$Fuma)[1] <- "0"
levels(df$Fuma)[2] <- "1"
df

Presentación de Tablas e Informes bonitos

Vamos a ver distintas formas de presentar una salida de una tabla resumen mejorando el aspecto básico.

resumen1%>%
  kbl() %>%kable_material(full_width=FALSE)
CatPeso Media_forma n stdev
D 0.0223 6 0.0023
N 0.0216 90 0.0016
OB 0.0177 27 0.0012
SO 0.0195 27 0.0019
resumen1 %>%
  kbl() %>%
  kable_paper("hover", full_width = F)
CatPeso Media_forma n stdev
D 0.0223 6 0.0023
N 0.0216 90 0.0016
OB 0.0177 27 0.0012
SO 0.0195 27 0.0019
resumen1 %>%
kbl(caption = "Hacemos una Tabla de Mejor Estilo") %>%
  kable_classic(full_width = F, html_font = "Cambria")
Hacemos una Tabla de Mejor Estilo
CatPeso Media_forma n stdev
D 0.0223 6 0.0023
N 0.0216 90 0.0016
OB 0.0177 27 0.0012
SO 0.0195 27 0.0019
resumen1 %>%
  kbl() %>%
  kable_classic_2(full_width = F)
CatPeso Media_forma n stdev
D 0.0223 6 0.0023
N 0.0216 90 0.0016
OB 0.0177 27 0.0012
SO 0.0195 27 0.0019

Estilo Oscuro

resumen1 %>%
  kbl() %>%
  kable_material_dark()
CatPeso Media_forma n stdev
D 0.0223 6 0.0023
N 0.0216 90 0.0016
OB 0.0177 27 0.0012
SO 0.0195 27 0.0019

tidyr

gather()

spread()

separate()

unite()

dplyr

select()

filter()

group_by()

summarise()

arrange()

join()

mutate()

El archivo flights está disponible en R y contiene información relativa a salidas de vuelos de NYC en 2013

flights %>%
   select(year, month, day, hour, minute)

Armamos una fecha a partir de tres variables

flights %>%
  select(year, month, day, hour, minute) %>%
    mutate(hora_partida = make_datetime(year, month, day,hour,minute)
  )

Fusiona tres columnas: year, month y day en una nueva columna “fecha”.


vuelos<-flights %>% unite("fecha", year,month, day, sep = "/")
vuelos

La función separate() separa en dos columnas la fecha

vuelos %>%
  separate(fecha, into = c("año", "mes"))

vuelos %>%
  separate(fecha, into = c("año", "mes"))%>%
 unite("campo",año,mes,sep="-")

Vamos a ver como darle nueva forma a una base de datos según el análisis que querramos implementar puede ser necesario

#Cargamos dos bases de datos: la primera tiene el nombre y el apellido de los músicos conjuntamente con el 
#instrumento que ejecutan principalmente.

artistsKH <- tibble(first = c("Jimmy", "George", "Mick", "Tom", "Davy", "John", "Paul", "Jimmy", "Joe", "Elvis", "Keith", "Paul", "Ringo", "Joe", "Brian", "Nancy"), last = c("Buffett", "Harrison", "Jagger", "Jones", "Jones", "Lennon", "McCartney", "Page", "Perry", "Presley", "Richards", "Simon", "Starr", "Walsh", "Wilson", "Wilson"), instrument = c("Guitar", "Guitar", "Vocals", "Vocals", "Vocals", "Guitar", "Bass", "Guitar", "Guitar", "Vocals", "Guitar", "Guitar", "Drums", "Guitar", "Vocals", "Vocals"))
artists <- artistsKH
artists%>%head()
# La segunda base tiene el nombre y el apellido de los artistas conjuntamente con la banda donde se hicieron famosos.

bandsKH <- tibble(first = c("John", "John Paul", "Jimmy", "Robert", "George", "John", "Paul", "Ringo", "Jimmy", "Mick", "Keith", "Charlie", "Ronnie"), last = c("Bonham", "Jones", "Page", "Plant", "Harrison", "Lennon", "McCartney", "Starr", "Buffett", "Jagger", "Richards", "Watts", "Wood"), band = c("Led Zeppelin", "Led Zeppelin", "Led Zeppelin", "Led Zeppelin", "The Beatles", "The Beatles", "The Beatles", "The Beatles", "The Coral Reefers", "The Rolling Stones", "The Rolling Stones", "The Rolling Stones", "The Rolling Stones"))
bandsKH%>%head()

Juntamos dos bases utilizando una o dos variables en común con left_join()

bands2 <- left_join(bandsKH, artists, by = c("first", "last"))
bands2 %>% head()

Observen Qué diferencia hay con right_join()


bands3 <- right_join(artists, bandsKH, by = c("first", "last"))
bands3 %>%head()
artists
bandsKH
# inner_join solo retiene las filas que estan en ambas bases
bands4 <- inner_join(bandsKH, artists, by = c("first", "last"))
bands4 
# full_join retiene las filas de cualquiera de las bases aunque no estén en la otra

bands5 <- full_join(bandsKH, artists, by = c("first", "last"))
bands5

Pasando de formato ancho a formato largo y viceversa

# En esta primera base hay tres columnas para cada empleado en las columnas están los salarios medios de tres años de trabajo
data_2 <- data.frame("names" = c("Pedro", "Carla", "Marta"), 
                      "W_2014" = c(100, 400, 200), 
                      "W_2015" = c(500, 600, 700),
                      "W_2016" = c(200, 250, 900) )

data_2
# En esta segunda base estan los mismos datos pero ahora para cada año y para cada empleado hay un registro donde esta su salario
data_3 <- data.frame(names = c("Pedro", "Carla", "Marta", "Pedro", "Carla", "Marta", "Pedro", "Carla", "Marta"),  year = c("2014", "2014", "2014",  "2015", "2015", "2015",  "2016", "2016", "2016"), salario = c(100, 400, 200, 500, 600, 700, 200, 250,900))

data_3

La funcióm gather() transforma los datos de formato ancho(wide) a formato largo(long)

data_wide <- data_2   #- data_2 está en formato ancho (wide)

data_long <- data_wide %>% gather(periodo, salario, 2:4) # 2:4 son las columnas que va a pasar al otro formato
# periodo y salario son los nuevos nombres de esas columnas
data_long

Si me molesta W_ y lo quiero sacar…puedo!!


data_long <- data_long %>% mutate(periodo = str_replace(periodo, "W_", "" ))# le indico que en la columna periodo reemplace w_ por nada
data_long
data_long$periodo <- gsub("W_", ".", data_long$periodo)

Pasar pasar de formato largo a formato ancho, tidyr tiene la función spread()

data_wide2 <- data_long %>% spread(periodo, salario)
data_wide2

Ejerccio 4

Con las bases de datos Clientes.xlsx y Registros.xlsx se pide:

  1. Separar la variable Cliente en dos variables: Apellido y Nombre.
  2. Unir ambas variables en una sola que se llame ID y separar apellido y nombre con “-”.
  3. Unir las bases de Clientes y Registros dejando las fechas al final y guardarlo en una nueva base.
  4. Ídem pero dejando la fecha al lado del nombre y guardarlo en una nueva base.
  5. Seleccionar de la ultima base, los registros correspondientes a PIMES y elegir a los últimos 10 que se atendió, guardarlo en una tabla y presentar una tabla bonita como salida

Ejercicio 5

Con los datos de la base consumo_oxigeno.xlsx a) Colocar en una variable el día y en la otra el consumo. Guardar en una nueva base de datos. b) A la nueva base de datos volverla al formato anterior.

LS0tDQp0aXRsZTogIkNsYXNlIDEgSW50cm8gUiBBdXN0cmFsIg0KYXV0aG9yOiAiRMOpYm9yYSBDaGFuIg0KZGF0ZTogIkFicmlsLTIzIg0Kb3V0cHV0Og0KICAgaHRtbF9kb2N1bWVudDoNCiAgICAgdG9jOiB5ZXMNCiAgICAgY29kZV9mb2xkaW5nOiBzaG93DQogICAgIHRvY19mbG9hdDogeWVzDQogICAgIGRmX3ByaW50OiBwYWdlZA0KICAgICB0aGVtZTogdW5pdGVkDQogICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCmVkaXRvcl9vcHRpb25zOiANCiAgbWFya2Rvd246IA0KICAgIHdyYXA6IDcyDQotLS0NCiFbKipDdXJzbyBSOiBDbGFzZSAxKipdKGRhdGE6aW1hZ2UvanBlZztiYXNlNjQsLzlqLzRBQVFTa1pKUmdBQkFRQUFBUUFCQUFELzJ3Q0VBQW9IQ0JVU0VoUVZGaFVaRlJZWkdCb2FHaHdTR0JvaEdCa1ZKUmtjR2h3ZUhoWWRJUzRtSGg4dExSZ2pKamdtS3k4eE5UVTFHaVk5UURzMFB6QTBOVFlCREF3TUVBOFFIeElTSHpRckpTdzNORFUwTkRFM05UUTBORFUyTkRRL05EUTBQVFEwTkRRL05ERTBNVFEwTkRRME5EUXhORFEwTkRRME5EUTBORFkwTlAvQUFCRUlBT0VBNEFNQklnQUNFUUVERVFIL3hBQWNBQUVBQWdNQkFRRUFBQUFBQUFBQUFBQUFCUWNEQkFZQkFnai94QUErRUFBQ0FRTURBZ1FEQXdnTEFBTUFBQUFCQWdBREJCRUZFaUV4UVFZVElsRUhZWUV5UW5FVUl6UnljNUdoc1JVek5WSmlkSUtEc3JQd2tzUFIvOFFBR2dFQkFRRUJBUUVCQUFBQUFBQUFBQUFBQUFFQ0F3UUZCdi9FQUNvUkFRRUFBZ0lDQWdBRkF3VUFBQUFBQUFBQkFoRURFaUV4QkVFVE1tRnhrVkdCOEJRaUl6T3gvOW9BREFNQkFBSVJBeEVBUHdDdVlpSitnZWNpSWdJaUlDSWlBaUlnSWlJQ0lpQWlJZ0lpSUNJaUFpSWdJaUlDSWlBaUlnSWlJQ0lpQWlJZ0lpSUNJaUFpSWdJaUlDSWlBaUlnSWlJQ0lpQWlJZ0lpSUNJaUFpSWdJaUlDSWlBaUlnSWlJQ0lpQWlmU3FTUUFNa25BQTZrOWhqM2tucG1tbzlRVTNZaW8yUWlJVktzK3plZ2FvQ1FBeDlIQUp5UjBrdVVudFVWRTZUdzFhMHFvY3ZUWEg1VGJvUE1CSjJ1S3dLYnhqYVNVUWJ1eDVrZnBPanZjVkNuTk5RU0dabEoyRUFrakhHU01jOUFPL1VaejNtN3Y2VFNMaWVxQ1JuQnh4bjVaNlpQMC9oUEpzSWlJQ0lpQWlJZ0lpSUNJaUFpSWdJaUlDSWlBaUlnSWlJQ0NZbWV5dW1vMUVxSWNNaHlQNUVmVVpIMWdUR2sySllicUQwNmxUWUhBK3pXcFZrTzlkcU5qZWgyaFNWM2NrWkFBbXRlYXVIU2x0VkE2T3RSV1NoVHA3Q0Fjb3V3ZXRTY05rampBNG5tdDNxVkRoVXBjN1dEb20xOXBYTzExWENGODlXQzlmcklxY3NjZCtjbDJ5MTdoNmpNenNXWmlXWTlNdDc0SEV6Mm1vUFRaVyszdFdvcWh5U0ZEcVZjcnp3ZlVmcjJtbkU2WEdYd0ppczRGQktWdmtxNVh6T25tMWEzTzFEVEJMYkYrN2pJSmJPYzhEUzFDejhsd2haR2ZBM2hEblkvM2tMZEN3NzdTUm5JN1RIYTNUMG1MSTVSaXJMbFRnN1dHRHoyNjllc2xucjBLVnNVUXEvbVZTZHZCY1VmSjJydmJiNlNITzRLT3ZQYkJuUHpqUkJSRVRxaEVSQVJFUUVSRUJFUkFSRVFFUkVCRVJBUkVRRVJFRFBaVy9tVkVUZXFiampkVU9FQjdaUFlkdnJOeTgwNnZiVTNEb3V4M1ZkNnNqamN1NDdWZFNjZHlSd2ZRUGFZdE5aRTN2VXR6WFFZWDdicXFPY2tFbE9wNHdBU0FlWmsxbThTb1VLQlZHd0FoRWRUd1NGMzduZmN3SGZQUTRuTzI5dGZTbzBtZGo0YytIdHpkNGR2ekZQS0hOUVpaMFpkMjVBT0R3UjFJNVB5blMvRGp3TzZPTHE1WGFSdlZLVHFNL2VSbWNFZDg4WTdmakxTUk1BQUFBRGdBZEFKNU9mNWRsNjRmeTNqai9WWHR2OEtiVUlxdFVxT3dZa3NwQXlwVURianBnSDFaNjlwQVhud3RxL2xGWUl3RnVFWjZUWnk1ZkEyVTJISGZQSTdBZHpMUTBtMHJVMnJtclc4MFBVTElOb0d4Tm9HMytIL3N4cmx2WHFVdHR2VVdrKzVEdWRkdzJoZ1dHTWp0Ky9weG5JODJQeU9TWDh6VnhqODhhcm8xeGFNRnIwbnBrNHdXR1ZKeG5BZGNxVHowQjRtcGJWalRkWEFWc2RuR1ZJeGdnanVDRGlmbzdYOUgvTExSN2RuMmx3b0xxb3p3d1k0QjZaMi94bjUrMTJ6VzJ1NjlKUVN0T29WWHpBQ1NvT1J1SFE1L2lEUGQ4ZjVINHNzdnR6eXgwMXpZT3RGYXpEQ00yMWR4QVordVdWZXJLTVlMRGpKQTk1cXpvcXlWYmsxbmY4bkpXbnVkdHh5aWhmUUJ0WXFuWlZYams0QW5PejBZWlcrMmFSRVRRUkVRRVJFQkVSQVJFUUVSRUJFUkFSRVFFUkVDUzB5N1NrQzVTbzdodnVWTmliQ0FBSEFRbHNrSGpJQjQ2ellzYVhuYWhSUnJjSUhxSnVvb1NnMm5CSUJkc3FTUFYxSFhqR1JQdnc5cVQweDVhVTY5VjJkbUMyOVJreUNnWDFLcU1YSXdTRHh0SnpOYS91SHBYUXFlVTlKMFpIMlhPNTIzREJ5eGNBdUNSM25DN3VWbjZOUDBxdlNhZHBxVktzOVZFcUs3VTIydUZJSlU0VnUzNncrdVIybWhvSGlTaGQwZzlPb3B3VlJzcXlnVkNCNlFHK1pIVFBVVFR0ZGZzRXZLOXVySWxkbVVzU0FCVWZZb0FEOUN3QUEyOWNnOFQ1SFRMek5WMTI2bVJOL1h1VnIyNjA2U05SWXQ1cnM1RElBcEl3dTA5L256MDQ2eUgxM3hlMWtqZWRRWlgyZzA5cExVcTFUZGcwMXFLdVEyTUgxS0NjbkE0bTlaNitWdEJjWGxNV1h1dFJ3VDE5T01ET1QvQUhjWitVZGJyWnRQeWgvaWhwS1cxOFdSdDNuQTFHRE1DeXNYT2VBQmhUMjVQMldsbjJuajNUNmliL3loRTUyN2F2cGZyZ0hZZWR2ejZjODRsVmZFSHhFbC9jSzFOM0tJR1hhNFhZR3pnc2hYa3F3QU9XbnErSmhuanlldFQ3WnlzMDE3VU9hTkJBTE5VQkZURlc0Vldkejk2cFROUVpiSEFCSEE2RGs1NXdqQk1tYkZyZmFxclNScTRJM2k3cU9FSjYraFVLZ2ZnNVAxa05uTStoeHpWcm5Ya1JFNklSRVFFUkVCRVJBUkVRRVJFQkVSQVJFUUVSRURlMDI0Vk42TzcwbGRRcGVrdTVnQTJjRmR5NVE5d0dIUWRla3kzNksxTkhwSlZhZ25vTlNxb0c1MkxOZ0FFaFFNY0xra2QrczBLRlVvNnVNRXFRY01BVk9EMEtuZ2c5Q0pOVkc4OFV2T3JYRmF0VkI4dEtLcVVwcnVaUjZPNUpRbllvWEFBNW5MTHhsdFlnUU9jOXgwUGNmV01UTGMyN1U2akk0d3lNVk9Ea1pIc2U0bjNiMnBxZmZSQjcxSENqOTNKUDBFNmVQYUxxK0dPcU5lV08ydCtjZWhVMkJuNUpBQ3VqRW43dzNZejE0bGRmRXJWM3VOUXFvU2ZMb05zcHIyQkFHOXNlNU9SbjJBbGdmQzJuUm9Xam9LOUdyVU5ScWppaytRb3dGR2NnSG91YzQ3emdQSE5qU2UrdUt0RzV0M1YyRGJSVTlTdmdCaG5HM3FNL2E3ejUvRDFuUGZIN09sL0s1R1pyTzNOUjBRWTlSeDZtQ2pIVTVjOEx4M014dWhCSVBVZXhCSDd4d1pNNkxZTzFOcXFxaHdTaGF1S1JvVTFBVmlhbm1BNDNic0xnWjRPTXozNTNVMnhHNXFtb1ZBSFN2VFlOaXBzRnhSUm1DdGhhZXlydHlvVVpJSUpCbk1TWDF2VUhiRkZnRkNNQ3dSeTFObjJCUXlKOWxGSTUycng2dndBaUpuam1vVWlJblJDSWlBaUlnSWlJQ0lpQWlJZ0lpSUNJaUFpSWdKSTJOKzZvMUVWUlJSc2xuQ25lVnhrcHZVYnRwNjdjZ0VubnJJNkpMSlo1VkphaFowNlZLaVFYTlJ3WHc0VUJhUFJDVUdTcFlna0RjZlNBZnZDUnhHT0R3Zm5KU3dyVXFsUXRjRXU0d1J2YkNWTnFCVVIyUDJGNEdXNXlCamc4ekxTb203OCt2VnFIY3JJRHNWZUVaaXBxRmVQemFlbGNEa2JsNkNZbVhYMmEyaGYvZlNKOU12Snh5Qm5rZE1lLzRTWDB2Um1xMHpWU205YkRNTmxJYnNNb1ZnSEFZTUZmT0J0NTRQUGFhdG1NM1JvMk5tS2pCWGNVZ3diWXpnaEdjRGhkK01LTWtaWThETWtidW9RcE54Uk5PNHBuWWpvcUtXZEFtRXFVOGJXQVZnUTRISXdQVU9SOFgxZTNlZ05tVWRYM2VVK1dDN3Z0aEg2RlBTdnBJRERCKzFuTWpyeTdlcXdMSDdLaEZVRTdVUWRGWEpKQUg0ekdybmRucGlkeXhMTWNrbkpKNmt6NGlKMVFpSWdJaUlDSWlBaUlnSWlJQ0lpQWlJZ0lpVDFob2FWVzA1UzdEOHFhb3JZQTlHMm9hWTIrL1RQTXpsbE1mYW9HSnY2UFpwV3VWcE81UkQ1bTUxR1NxclRkODdlLzJPa3phcG9yMjFQYzV5M21sRks4bzlQWXJxNk4zRGJ2OEF4ekhmSGZWTklxSklYV25oTFczcjdpVFZhc3BVNHd1eGxBSVB6M2Z3bTNRMFZYbzBhNFp2TEsxdk9PQm1teURjRkh2dkRMdHozYjVTZmlZei93QVhTRW0xU3YzUkdRTmhTcklmU3VkakVNeWhpTWdFakpBbTM0ZjAxYmw2aXNXR3lnOVhGUGJ1WXJ0OUlMY0RPN3FacDNGRUxWS0FNbzNCY1ZOcFlkTTVLOGQrMHR5bHVoaXAxblRkdGRsM0RhMjFpTnkreHgxSHlNOXRxNXB0dkNxV0gyUzY1MnRrRU12c3dJL25KN3hUNGVTeXdBN2x2TmRBdFJWQmRGQS9PS1ZQMk1uYmdqT1IzbXRvdWtwWHBWV3k3dWhINXFpVURtbmdscWdEZmJDa0FGVjU1ek16UEM0OXZvMWQ2UTdNU1NUeVNja251ZTgrWkk2RFlDNnJyU1ppb0tPMlZBejZhYlAwUHZ0eDlZMEd4VzVxTXJzeXF0S3JVT3pHZlFoZkF6eHpqRXR6azMraHBIUkpHLzA1VW8wYTlObWFsVVoxdzRBZEtpNDNLY0VoZ1F3SVllL1FUWXNOQ05XMXIxd3hESUN5THQ0ZEVLZWEyN3R0RGdnZDlyZTBmaVk2MmFRMFNSMEhUMXVhL2x1ekt1eW81S1l6NmFiUGpuam5iajZ6MiswOVZvVXJpbXpOVHFNNlljQU9sUlFDVk8wa01DR0JCR1B3RWQ1MjZwcEd4RVRZUkVRRVJFQkVSQVJFUUVTWjhJNlorVlgxdlNLaGxMNWNObkhscU56WndRZWd4K0pFN240a2VEYmVoYXJYdHFZVFl3RGhTeERJM0FQSlBRNCtoTTQ1OCtPT2N3djJzeDNOcXRrenAzaUtwUVdpQWxKMm9PelVtcUt4Wk54M01CaGdDTThqSU9NOFNHbHNlRHZEbG5YMG9YRlMzUnFpcld5MjV4dTJzd1VrQnZaUm1Ubnp4d2t1VTJZeTMwcSsydW1wMU42Z2JzT1BVRGpEb3lOeG4yYzQra3lWdFNxUFFwMjdOdXAwMlprQjZydUFCQVA5M2pPT3hKbW1Heno3ODhkSlp2d3c4UFdsN2JWV3IwRmRxZGJZR0xPQ1YySzNPR0E0M0dYbHl4d3g3MkVtN3BYdGErWjZOS2ljYktUT3lZQjNaY3FXeWM4L1pHT0o1UjFDb2xHclFWc1U2akl6cjdzbWR2NGRlZmZhUGFUT2o2RUwzVW50MXhTVHpLdWRuM0tTTVJoYzU1NEE1OTh6cS9GRjVZNlRWVzNwNmRTcnY1YXV6M0J5Y0VzQU56S3hZK2s1NkNaeTVaTE1aanUzeXNuMnIzVHRRYWd6c3FvKyttOUpoVURGU2pZM0Qwc3BCNDY1bUI2b0xoZ2lKakdGcDd0dkg2ekU5dmVkUHErc1dGMWExTnRtbHBkS1VLR2tjbzQzZ01PQXVEZ2s0STdkWmwwdlZkSXAwYWExYk9yWHFoUnZjRUFNL1U0SG1EZ2RCeDJsNzMzMXV6WDZvTy8xNnJjTFZXb0VZVktwcmZaUDV1b1JoaWgzZWtNT29PUWNlOHhhWHFqV3pLeUlocUkyOUhjUHZSc1krNndERDVNQ09UN3kyUEMyaDZWcU5FMWtzaWlxNVRGUm0zWkFCejZYSXg2cHlsVFc5RVhjUDZPcUVqSSsxd1QrUG1kUHBPV1BMamQ0VEcrUGMveWxuM3R4dW0zNzI5VVZVd1hBY2VzRWpESVViZ0VjNFl6elRMOXJaeTZoV0pSMElxQWxTcktVWWVrZzlDZWN6cWZBdHRiMzJwT3RTM1FVbnBzeTAwTGJVS2xNWVlFRW5rNUo2NXp4Snp4TWRKc0x2OG5xYWVYRzFITFU2alpBWWtmWUxET052dk5aOHVNeTY5YmJvbU8vS3U3elVHcXBUUWhVcDA5MnhLWUlRRmpsbTlSSlpqZ1pKSjZDYkZucmxhaTlKa1lBVTFLQlBWNWJLZHhmZXVjTVczdGsvd0Q0SjNmaVB3TGJQWm05c1dZS0U4ell4TEs2WXlkcGIxS3dIYk9PTWNkWldNNmNXZUhKajRuOWtzc2JlbTM3VzFUekVWQ2RyTGh3U3UxbEtzTUJnZWpFZFo3ZDZnMVJLZFBDSlRwN3RpVXdRb1pzYm1PNGxtWTRISko2ZHA1cFZvMWV2U3BJTXM3cW9CNmNrWno4Z01rL0lHV3Q0NDhEV3lXTlNwYlVnbFNuNnpzWmp1UVpEcmdrOXNuOFZtZVRrd3d6a3M4MVpMWXA2SWxsZkREdy9hM3RDdWJpZ3RSa3FBS3haZ2RwUUhCMnNCMXorK2RPWGxuSGoycVNidWxheE56V1VWYm00VkZDb3RWMVZWemhWVnlvSFBQYWFjM0x1YkNJaVZDSWlBaUo5MGFUVkdWRUc1MllLbzkySkFBL2VZSGUrQndMR3d2ZFNiN1JYeWFHZTdaSEkvRmlvLzJ6T2srRzErdC9wdFd6ckhlYVlOTnM5V29zRHRQMDVIK2tTUDhBRk9yMituVXJmVG10VXV4VHBxN2VZNVVDcHo2c0JUbGpsbStXNGU4MGZDbmpXMnBYU0tsaFR0aFZaYWJ2VHFNU0FUaGNxVUdSbkhmaWZOenh5emx5Nis3dVg5STZTeVhUZzlSc1d0NjFTaS8yNmJzaCtlRHdRUFlqQkh5TXVQNGZmMkVmMWJuL0FKdk9hK01PamJLOUs2VWVtb05qNDdWRkdWUDFYai9ST2wrR2krWm94UlQ2czNDZmd4WmlQNE1EOVpybXo3OE9OL1V4bXNsSXIwSDREK1V1UDRLZm9sei9BSmovQU90SlR1d3I2U01NT0NEMUREZ2cvT1hOOEdLTExaMTJJd3IxeVZKKzhBaUtTUGxrRWZTZGZsMy9BSXY0VEg4eXRiSFdIc3RSZTRRYmlsYXFHVS9lUXV3WmM5dU8vdUJMVnY4QVQ3THhCYnJVcHZ0cUlNQmhqektaNjdIVHVQbDlRWlRtb1VIYXJkT3FrcWxXcHZJNktHcU1GejdBbmpNK05MMUtyYTFGcTBYS092Y2RDUFpsNk12eU11ZkQyMWxoZFpRbDE3YjNpUHc1Y2FmVUMxbDlMWjJ1bVNqajVIc2Y4SjUvSHJJYVhuVXU2ZXE2SlVxMVZDNXBWR1BzdFdudTlTazlzcmtmSTRsR0NiK1B5NVp5ekwzUEZaeW1sMWZCdjlBcS93Q1lmL2drcGl0OXR2MW0vbVpjL3dBRy93QkFxLzVoL3dEZ2twaXQ5dHYxbS9tWnkrUC9BTnViV1hxT3orRWY5cGo5alUvbWs2THg1NE11NzdVUE1wS2dwbW1pRm5jQUFndG4wakxIcjdUbmZoSC9BR21QMk5UK2FTUitJT3MxclBXRnEwbVliYVZJbGR4Mk9OejdsWmVoQkhHZTB6eWR2eC85bnZSTmRmTGQ4UStKNk9uMlA5RzBDMVNzdFB5blowWlZRTUR1YjFBWkp5Y0FjY2ptVlhMczhVNlBTMW14UzVvQUdzRTNVenhsdjcxSnZua0VmSWo4WlNqREdjZ2dqZ2dqa0h1TWU4MzhXNDliL1hmbE1uYy9ESzJXbTF6ZjFQNnUycHR0K2RRakp4OHd2SCs1T2srRlhpRnJuOHFvVmpsMmQ2Nmc5MWR2emk4OWdXSC9BTS9sTk8vdnFXa2FkYTJsVzNXNGVzcHExVWQ5cWc1RGNrS2M0T0ZBL3dBRWhkSThiMnR0V1dvbW0wcUpCd3owNnBMS2g0WWdiT2VPY2Q4VGpuTXVYdGxKNzlYOW1wZGVIT2VKOUlObGQxcUgzVmJLRTk2YmNyL0E0L0ZUTEorQ2Y5UmQvdFUvNnhNZnhnMGdQU28zaVlPM0NPUjNwdHlqZlFuSCs1TW53VS9xTHY4QWFyLzFpWGs1Ty94OS9malpKckpWMnRmcFZ6KzNxLzhBWTAwcHU2MStsWFA3ZXIvMk5OS2U3SDhzWUlpSnBDSWlBa3g0WDFLamEzS1Y2cVBVQ1paRnA3ZVh4Z0VsajBHYy9qaVE4U1pZekthcEcvcmVvRzZ1cTljNS9PT3pBTjFDWndpOGV5Z0Q2VFFJaUpKTlRVVllPcStQS04xcDR0YTFHb3orV2c4d0ZNZWFvR0h4bk9NamtleE1odkJYaTk5TmRnVjh5aTVCZFFRR0RBWTNxVHhuSEJCNE9CMHhPWGljNXdZVEc0NjhVN1ZaT3BlSU5EcnMxZDdTcTFWdVdWUVZETi9pQzFBaFB1ZTgyTk0rS2RPbUdWclVxZ0lGSktCUUJLWUFBQnpqSnprOERBbFhSTS82WERXcnUvM1h2WFQ2QjRtUzB1N21vYVBuVUxqY0hSOXU3YXpseDdxU01rWTZIUGFTRFZ0Q2M3dkx1NldlZGlFRmZ3QjNIQS9BemlJbXN1REczY3RuN1ZKazdYeEg0MVNwYkN5czZKb1c0RzF0eEc5bHprcmdFNEI2a2trbm42OFZFVGZIeDQ0VFdKYnQzdmdyeDFSMDYyTkZxTlNvek96a3FVQzhnS0FNblBSWncxd3lsM0s1MmxtSzdzYnRwT1JuSEdaamlURGp4eHl1VTkwdDI2RHdYcnFhZmRHNGRIZjBNZ1ZObzVZcmtrc2Y4UDhBR2UrTmRlcDZoY2l1aU1ub1ZHVnlwNVVzUVFWL1cvaE9laVB3c2UvZjdOK05PcThGZU1uMDRzaFExYURuSlVOZ3EvVGNwUEhQUWp2Z2ZYTHF1dTJGeGVVN29XOVpDSEQxRVVwdHFFY2c5ZlNTUU05aU05K1p5RVNYaHd1VnkrenRVejRzMXMzOTI5ZkRLcENxaXZqS0tGQXh4OHl4L3dCVWhvaWJ4eG1NMUJZRmo0OG8vd0JIQ3l1S05TcitiTk1zaFVlbm5aakp6bFJqbjNXYXZnWHhwUzAybFZScVZTb3p2dnlwUUFLRkNnY25yeE9KaWM3OGZEVm4xZkoycnQ3alg5SGQyZHROcWxtWXN4ODVobGljazRGVEhVelQxZlc5UGUyZW5iV0pvVkgyanpISVpsVU1HWUJpeFlFZ1k0OTV5a1JPSEdmZC9tbmFrUkU3SVJFUUVSRUJFUkFSRVFFUkVCRVJBUkVRRVJFQkVSQVJFUUVSRUJFUkFSRVFFUkVCRVJBUkVRRVJFQkVSQVJFUUVSRUJFUkFSRVFFUkVCRVJBUkVRRVJFQkVSQVJFUUVSRUJFUkFSRVFFUkVCRVJBUkVRRVJFQkVSQVJFUUVSRUJFUkFSRVFFUkVCRVJBLy9aKXt3aWR0aD13aWR0aD0nMTAwcHgnIGhlaWdodD13aWR0aD0nMTAwcHgnfQ0KDQoNCiMgVmlzacOzbiBHZW5lcmFsIGRlIHVuYSBiYXNlDQoNCiMjIENhcmdhbW9zIGJpYmxpb3RlY2FzIChsaWJyYXJ5KQ0KDQoNCg0KDQoNCg0KYGBge3Isd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFLGNhY2hlPUZBTFNFfQ0KDQojIVtUZXh0byBBbHRlcm5hdGl2b10oVVJMX2RlX2xhX2ltYWdlbikNCiMhWyoqQ3Vyc28gUjogQ2xhc2UgMSoqXShsb2dvYXVzdHJhbC5wbmcpe3dpZHRoPXdpZHRoPScxMDBweCcgaGVpZ2h0PXdpZHRoPScxMDBweCd9DQoNCmxpYnJhcnkocmVhZHhsKSAjbGVjdHVyYSBkZSBhcmNoaXZvcyBlbiBFeGNlbA0KbGlicmFyeShyZWFkcikgIyBwYXJhIGxlZXIgZGF0b3MgZGUgdGV4dG8gcmVjdGFuZ3VsYXJlcw0KbGlicmFyeShkcGx5cikgIyBtYW5lam8gZGUgZGF0b3MNCmxpYnJhcnkodGlkeXZlcnNlKSAjIG1hbmVqbyBkZSBkYXRvcw0KbGlicmFyeShEVCkNCmxpYnJhcnkoa2FibGVFeHRyYSkgIyBoYWNlciB0YWJsYXMgYm9uaXRhcw0KbGlicmFyeShsdWJyaWRhdGUpICMgbWFuZWpvIGRlIGZlY2hhcw0KbGlicmFyeShueWNmbGlnaHRzMTMpICMgYXJjaGl2byBkZSBkYXRvcyBkZSB2dWVsb3MNCmxpYnJhcnkoZ29vZ2xlc2hlZXRzNCkgIyBwYXJhIGxlZXIgZGVzZGUgZWwgR29vZ2xlIERyaXZlDQpsaWJyYXJ5KGZvcmVpZ24pDQpsaWJyYXJ5KHV0ZjgpDQoNCmBgYA0KDQoNCg0KDQoNCg0KDQpgYGB7cix3YXJuaW5nPUZBTFNFLGluY2x1ZGU9RkFMU0V9DQpTeXMuc2V0bG9jYWxlKCJMQ19BTEwiLCAiRVNfRVMuVVRGLTgiKSMgcGFyYSBsYXMgdGlsZGVzDQpgYGANCg0KIyBMb3MgQ2h1bmtzIA0KDQoNCkVuIGxvcyBDaHVua3Mgbm9zIHB1ZWRlIGludGVyZXNhciBjb25maWd1cmFyIHF1ZSBubyBub3MgZW52aWUgbWVuc2FqZXMgbyBxdWUgbm8gbXVlc3RyZSBlbCBjw7NkaWdvIG8gYmllbiBxdWUgbm8gbXVlc3RyZSBsYSBlamVjdWNpw7NuIGRlIHVuIGPDs2RpZ28uIEEgY29udGludWFjacOzbiBjw7NtbyBoYWNlcmxvOg0KDQoNCiAtICoqY2FjaGU9RkFMU0UqKiwgKiptZXNzYWdlPUZBTFNFKiosICoqd2FybmluZz1GQUxTRSoqLCBzaXJ2ZW4gcGFyYSBxdWUgbG9zIGNvbWFuZG9zIHF1ZSBtYW5kYW4gbWVuc2FqZXMgbm8gbG9zIGltcHJpbWFuIGVuIHBhbnRhbGxhLiBFc3RvIGVzIGltcG9ydGFudGUgc2kgcXVlcmVtb3MgcXVlIG51ZXN0cm9zIGxlY3RvcmVzIG5vIHNlcGFuIGxvIHF1ZSBlc3TDoSBjb3JyaWVuZG8gcG9yIGRldHLDoXMuDQoNCiAtICoqZXZhbCA9IEZBTFNFKiosIHNpcnZlIHBhcmEgcXVlIGxvcyBjw7NkaWdvcyBzZSBtdWVzdHJlbiwgcGVybyBxdWUgbm8gc2UgZWplY3V0ZW4uIEVzdG8gZXMgw7p0aWwgY3VhbmRvIGVzY3JpYmltb3MgdHV0b3JpYWxlcyBkZSBSIHBlcm8gbm8gcXVlcmVtb3MgcXVlIHNlIGVqZWN1dGUgbG8gcXVlIGhheSBkZW50cm8uDQoNCiAtICoqZWNobyA9IEZBTFNFKiosIHNpcnZlIHBhcmEgcXVlIGxvcyBjw7NkaWdvcyBzZSBlamVjdXRlbiBwZXJvIG5vIHNlIG11ZXN0cmVuLiBFc3RvIGVzIMO6dGlsIGN1YW5kbyBlc3RhbW9zIGVzY3JpYmllbmRvIHVuIGFydMOtY3VsbyB5IHF1ZXJlbW9zIHF1ZSBudWVzdHJvIGPDs2RpZ28gc2UgZWplY3V0ZSAocG9yIGVqZW1wbG8sIHBhcmEgY3JlYXIgdW5hIGdyw6FmaWNhIG8gdW4gbWFwYSkgcGVybyBubyBxdWVyZW1vcyBxdWUgbG9zIGxlY3RvcmVzIHZlYW4gbGFzIHRyaXBhcywgbyBlbCBjw7NkaWdvIHF1ZSBsbyBnZW5lcmEuDQoNCg0KDQojIyBMZWFtb3MgdW4gYXJjaGl2byB4bHN4IGRlc2RlIHVuYSBydXRhIHkgbG8gdmlzdWFsaXphbW9zDQoNCmBgYHtyLGNvbGxhcHNlPVRSVUV9DQpiYXNlSU1DIDwtIHJlYWRfZXhjZWwoIi4uLy4uL0lNQ2luZmFudGlsLnhsc3giKQ0KDQoNCmJhc2VJTUMNCmBgYA0KDQoNCg0KIyMgY8OzbW8gcG9kZW1vcyBhY2NlZGVyIGEgdW5hIGNvbHVtbmEgKHZhcmlhYmxlKSAtIG1vZG8gY2zDoXNpY28NCmBgYHtyLGNvbGxhcHNlPVRSVUV9DQpiYXNlSU1DWyw0XSAjIGFjY2VkZW1vcyBhIGxhIGN1YXJ0YSBjb2x1bW5hDQoNCmJhc2VJTUMkUEVTTyAjIGFjY2VkZW1vcyBhIGxhIG1pc21hIGNvbHVtbmEgcGVybyBwb3Igc3Ugbm9tYnJlDQoNCmBgYA0KIyMgY8OzbW8gcG9kZW1vcyBhY2NlZGVyIGEgdW5hIGZpbGEgKHJlZ2lzdHJvKS0gbW9kbyBjbMOhc2ljbw0KDQpgYGB7cixjb2xsYXBzZT1UUlVFfQ0KYmFzZUlNQ1szLF0NCmJhc2VJTUNbYmFzZUlNQyRQQUNJRU5URT09IjMiLF0NCmBgYA0KDQojIyBjw7NtbyBwb2RlbW9zIGFjY2VkZXIgYSB1biBkYXRvIChmaWxhIHkgY29sdW1uYSBlc3BlY2lmaWNhcykNCg0KYGBge3IsY29sbGFwc2U9VFJVRX0NCmJhc2VJTUNbMywyXQ0KYmFzZUlNQ1tiYXNlSU1DJFBBQ0lFTlRFPT0iMyIsMl0NCmBgYA0KDQoNClsqKkN1w6FsIGVzIGxhIGRpZmVyZW5jaWEgZW50cmUgYW1iYXMgc2FsaWRhcz8qKl17c3R5bGU9ImNvbG9yOnJlZCJ9DQoNCiMgTGVlbW9zIGRhdG9zIGRlc2RlIHVuYSB1cmwNCg0KYGBge3IsY29sbGFwc2U9VFJVRX0NCnVybC5kYXQgPC0gImh0dHA6Ly9iaXQubHkvRGF0YWJhc2UtRXN0dWRpYW50ZXMiICAgICMgTGEgdXJsIGRlc2RlIGRyaXZlDQplc3R1ZGlhbnRlcyA8LSByZWFkLmRlbGltKHVybC5kYXQpIA0KZXN0dWRpYW50ZXMNCmBgYA0KDQoNCg0KDQoNCiMgQWhvcmEgdW5hIGZvcm1hIG3DoXMgbW9kZXJuYTogTGEgQmlibGlvdGVjYSBkcGx5cg0KDQoNCg0KKipFbCBwYXF1ZXRlIGRwbHlyIHByb3BvcmNpb25hIHVuYSBmb3JtYSBiYXN0YW50ZSDDoWdpbCBkZSBtYW5lamFyIGxvcyBmaWNoZXJvcyBkZSBkYXRvcyBkZSBSLiBBZGVtw6FzLCBlbCBjw7NkaWdvIGVzY3JpdG8gdXNhbmRvIGVzdGUgcGFxdWV0ZSAoZXNwZWNpYWxtZW50ZSB1c2FuZG8gbGEgc2ludGF4aXMgZW4gY2FkZW5hIHF1ZSB2ZXJlbW9zIG3DoXMgYWRlbGFudGUpIGVzIG11Y2hvIG3DoXMgbGVnaWJsZSB5IGbDoWNpbCBkZSBlbnRlbmRlcioqLiANCg0KDQoqKkVsIHBhcXVldGUgaW5jbHV5ZSB1biBjb25qdW50byBkZSBjb21hbmRvcyBxdWUgY29pbmNpZGVuIGNvbiBsYXMgYWNjaW9uZXMgbcOhcyBjb211bmVzIHF1ZSBzZSByZWFsaXphbiBzb2JyZSB1biBjb25qdW50byBkZSBkYXRvcyAoc2VsZWNjaW9uYXIgZmlsYXMgZmlsdGVyLCBzZWxlY2Npb25hciBjb2x1bW5hcyBzZWxlY3QsIG9yZGVuYXIgYXJyYW5nZSwgYcOxYWRpciBudWV2YXMgdmFyaWFibGVzIG11dGF0ZSwgcmVzdW1pciBtZWRpYW50ZSBhbGd1bmEgbWVkaWRhIG51bcOpcmljYSBzdW1tYXJpc2UpLiBMbyBxdWUgaGFjZSBxdWUgbGEgc2ludGF4aXMgc2VhIGVzcGVjaWFsbWVudGUgY2xhcmEgZXMgbGEgY29ycmVzcG9uZGVuY2lhIHRhbiBuw610aWRhIGVudHJlIGVsIGNvbWFuZG8geSBsYSBhY2Npw7NuLiBQYXJhIGxsZXZhciBhIGNhYm8gZXN0YXMgYWNjaW9uZXMgZGViZW1vcyB0ZW5lciBlbiBjdWVudGEgYWxndW5hcyBjYXJhY3RlcsOtc3RpY2FzIGNvbXVuZXM6KioNCg0KIC0gRWwgcHJpbWVyIGFyZ3VtZW50byBzaWVtcHJlIGVzIHVuIGRhdGEuZnJhbWUNCiAtIEVsIHJlc3RvIGRlIGFyZ3VtZW50b3MgaW5kaWNhbiBsbyBxdWUgcXVlcmVtb3MgaGFjZXIgY29uIGVsIGRhdGEuZnJhbWUuDQogLSBFbCByZXN1bHRhZG8gc2llbXByZSB0aWVuZSB0YW1iacOpbiBsYSBlc3RydWN0dXJhIGRlIGRhdGEuZnJhbWUNCg0KDQoNCg0KDQojIyBTZWxlY2Npb25hciBmaWxhczogZmlsdGVyKCkNCiBFc3RhIGFjY2nDs24gY29uc2lzdGUgZW4gc2VsZWNjaW9uYXIgbGFzIG9ic2VydmFjaW9uZXMgKGZpbGFzKSBxdWUgY3VtcGxlbiBsYXMgY29uZGljaW9uZXMgcXVlIG5vcyBpbnRlcmVzYW4uKioNCiANCipUcmVzIGVqZW1wbG9zKg0KDQogLSBlbCBwcmltZXIgY29tYW5kbyBzZWxlY2Npb25hIHRvZG9zIGxvcyBlc3R1ZGlhbnRlcyBjYXRlZ29yaXphZG9zIGNvbW8gYWx0b3MuDQogLSBlbCBzZWd1bmRvIHNlbGVjY2lvbmEgbG9zIGVzdHVkaWFudGVzIGNhdGVnb3JpemFkb3MgY29tbyBhbHRvcyBvIG1lZGlvcy4NCiAtIGVsIHRlcmNlcm8gc2VsZWNjaW9uYSBsb3MgZXN0dWRpYW50ZXMgYWx0b3MgbWVub3JlcyBhIDE3IGHDsW9zLg0KDQpgYGB7cixjb2xsYXBzZT1UUlVFfQ0KDQpmaWx0ZXIoZXN0dWRpYW50ZXMsIEVzdGF0dXJhPT0nQWx0YScpDQpmaWx0ZXIoZXN0dWRpYW50ZXMsIEVzdGF0dXJhPT0nQWx0YScgfCBFc3RhdHVyYT09J01lZGlhJykNCmZpbHRlcihlc3R1ZGlhbnRlcywgRXN0YXR1cmE9PSdBbHRhJywgRWRhZCA8IDE3KQ0KDQoNCg0KYGBgDQoNCiMjIFNlbGVjY2lvbmFyIGNvbHVtbmFzOiBzZWxlY3QoKQ0KIEVzdGEgYWNjacOzbiBjb25zaXN0ZSBlbiBlbGVnaXIgdW4gc3ViY29uanVudG8gZGUgbGFzIHZhcmlhYmxlcyAoY29sdW1uYXMpIGRlbCBmaWNoZXJvLiBQb3IgZWplbXBsbyBwb2RlbW9zIHNlbGVjY2lvbmFyIGRlIGxhIGJhc2UgZGUgZXN0dWRpYW50ZXMgbGFzIHZhcmlhYmxlcyBFc3RhdHVyYSB5IGVsIENvbGVnaW8uDQogDQpgYGB7cixjb2xsYXBzZT1UUlVFfQ0Kc2VsZWN0KGVzdHVkaWFudGVzLCBFc3RhdHVyYSwgQ29sZWdpbykNCmBgYA0KDQoNCg0KDQojIyBEb3MgZWplbXBsb3MNCiAtIEVzIHBvc2libGUgc2VsZWNjaW9uYXIgdW4gcmFuZ28gZGUgdmFyaWFibGVzIHV0aWxpemFuZG8gIjoiIA0KIC0gbyBlbGVnaXIgdG9kYXMgbGFzIHZhcmlhYmxlcyBtZW5vcyBhbGd1bmFzLg0KIC0gRWwgcHJpbWVyIGVqZW1wbG8gc2VsZWNjaW9uYSB0b2RhcyBsYXMgdmFyaWFibGVzIGVudHJlIENvbGVnaW8geSBQMw0KIC0gRWwgc2VndW5kbyBzZWxlY2Npb25hIHRvZGFzIG1lbm9zIFNleG8NCiAtIEVsIHRlcmNlcm8gZXhjbHV5ZSBsYXMgdmFyaWFibGVzIGRlc2RlIEdhc3RvcyBoYXN0YSBBR1BFUTIgaW5jbHVzaXZlDQogDQogDQpgYGB7cixjb2xsYXBzZT1UUlVFfQ0Kc2VsZWN0KGVzdHVkaWFudGVzLCBDb2xlZ2lvOlAzKQ0Kc2VsZWN0KGVzdHVkaWFudGVzLCAtU2V4bykNCnNlbGVjdChlc3R1ZGlhbnRlcywgLShHYXN0b3M6QUdQRVEyKSkNCmBgYA0KDQoNCg0KIE90cmEgcG9zaWJpbGlkYWQgZXMgc2VsZWNjaW9uYXIgbGFzIHZhcmlhYmxlcyBjdXlvIG5vbWJyZSANCiBjb250ZW5nYSBjaWVydG9zIHTDqXJtaW5vczoNCiANCiAtIEVsIHByaW1lciBlamVtcGxvIHNlbGVjY2lvbmEgbGFzIHZhcmlhYmxlcyBxdWUgZW4gc3Ugbm9tYnJlIGNvbnRpZW5lbiBsYSBwYWxhYnJhIFNleG8uDQogLSBFbCBzZWd1bmRvIGVqZW1wbG8gYSBsYXMgbWlzbWFzIHZhcmlhYmxlcyBsYXMgZXhjbHV5ZS4NCiANCmBgYHtyLGNvbGxhcHNlPVRSVUV9DQpzZWxlY3QoZXN0dWRpYW50ZXMsIGNvbnRhaW5zKCdTZXhvJykpIA0Kc2VsZWN0KGVzdHVkaWFudGVzLCAtY29udGFpbnMoJ1NleG8nKSkgDQpgYGANCmBgYHtyLGNvbGxhcHNlPVRSVUV9DQpzZWxlY3QoZXN0dWRpYW50ZXMsc3RhcnRzX3dpdGgoJ3AnKSkNCnNlbGVjdChlc3R1ZGlhbnRlcyxlbmRzX3dpdGgoJ3MnKSkNCmBgYA0KDQoNCg0KIyBPcmRlbmFyOiBhcnJhbmdlKCkNCiMjIE9yZGVuYSBsYXMgZmlsYXMgZGUgbWVub3IgYSBtYXlvciB2YWxvciBkZSBsYSB2YXJpYWJsZSBlbGVnaWRhLg0KDQoqKlNpIGVzY3JpYmltb3MgdW4gc2lnbm8gbWVub3MsIG9yZGVuYSBkZSBtYXlvciBhIG1lbm9yLioqIA0KDQpQb3IgZWplbXBsbyBwYXJhIG9yZGVuYXIgZGUgbGEgYmFzZSBkZSBlc3R1ZGlhbnRlcyBlbiBmdW5jacOzbiBkZSBsYSBFZGFkOg0KDQogLSBFbCBwcmltZXIgZWplbXBsbyBvcmRlbmEgbGEgYmFzZSBkZSBlc3R1ZGlhbnRlcyBlbiBmdW5jacOzbiBkZSBsYSBFZGFkIGRlIG1lbm9yIGEgbWF5b3INCiAtIEVsIHNlZ3VuZG8gZWplbXBsbyBsbyBoYWNlIGRlIG1heW9yIGEgbWVub3INCiAtIEVsIHRlcmNlciBlamVtcGxvIG9yZGVuYSBwcmltZXJvIHBvciBFZGFkICB5IHNlZ3VuZG8gcG9yIGxhIG5vdGEgZGUgbGEgZXZhbHVhY2nDs24gRmluYWwNCiAtIEVsIGN1YXJ0byBlamVtcGxvIGxvIG9yZGVuYSBlbiBmb3JtYSBjcmVjaWVudGUgZGUgZWRhZCAgeSBkZWNyZWNpZW50ZSBkZSBub3RhIGRlIGV2YWx1YWNpw7NuIGZpbmFsDQoNCmBgYHtyLGNvbGxhcHNlPVRSVUV9DQogICANCmFycmFuZ2UoZXN0dWRpYW50ZXMsIEVkYWQpIA0KYXJyYW5nZShlc3R1ZGlhbnRlcywgLUVkYWQpDQphcnJhbmdlKGVzdHVkaWFudGVzLCBFZGFkLCBGaW5hbCkNCmFycmFuZ2UoZXN0dWRpYW50ZXMsIEVkYWQsIC1GaW5hbCkNCmBgYA0KDQojIFNpbnRheGlzIGVuIGNhZGVuYQ0KDQpFbCBwYXF1ZXRlIGluY29ycG9yYSB1bmEgc2ludGF4aXMgZW5jYWRlbmFkYSBxdWUgcGVybWl0ZSBlc2NyaWJpciBsYXMgYWNjaW9uZXMNCmVuIHVuIG9yZGVuIG5hdHVyYWwsIGVuIG9wb3NpY2nDs24gYSBsYSBmb3JtYSBhbmlkYWRhIGVuIGxhIHF1ZSBsbyBoYXLDrWFtb3MgDQpub3JtYWxtZW50ZS4gDQoNCioqUHJpbWVybyBzZSBlc2NyaWJlIGVsIG5vbWJyZSBkZWwgZmljaGVybyB5IGx1ZWdvIGxhcyBhY2Npb25lcyoqDQoNCkVuIGVsIG9yZGVuIGVuIHF1ZSBzZSByZWFsaXphbiBzZXBhcmFkYXMgcG9yIGVsIG9wZXJhZG9yICU+JQ0KKHF1ZSBwb2Ryw61hbW9zIGxlZXIgY29tbyBlbnRvbmNlcyApLiBFc3RlIG9wZXJhZG9yIHNlIGRlbm9taW5hICJwaXBlIiB5IHNlIHRyYWR1Y2UgY29tbyBjYcOxby4gDQoqUG9yIGVqZW1wbG8sIHNpIHF1ZXJlbW9zIHNlbGVjY2lvbmFyIGxhcyB2YXJpYWJsZXMgcXVlIGNvbnRpZW5lbiANCmxhcyBtZWRpZGFzIGRlbCBww6l0YWxvLCBzZWxlY2Npb25hciBsb3MgbGlyaW9zIHBhcmEgbG9zIHF1ZSBsYSBsb25naXR1ZCBkZWwgcMOpdGFsbw0KZXMgbWF5b3IgcXVlIDQgbW0geSBvcmRlbmFybG9zIGRlIG1lbm9yIGEgbWF5b3IgbG9uZ2l0dWQgZGVsIHDDqXRhbG8sIA0KcG9kZW1vcyBlc2NyaWJpcjoqDQoNCg0KYGBge3IsY29sbGFwc2U9VFJVRX0gDQoNCg0KZXN0dWRpYW50ZXMgJT4lDQogIHNlbGVjdChjb250YWlucygnUCcpKSU+JQ0KICBzZWxlY3QoLWNvbnRhaW5zKCdBJykpICU+JQ0KICBmaWx0ZXIoUDEgPiA0KSU+JQ0KICBhcnJhbmdlKFAyKQ0KYGBgDQoNCg0KDQoNCg0KIyBQYXJhIHNlbGVjY2lvbmFyIGxhcyB2YXJpYWJsZXMgbnVtw6lyaWNhcyBzb2xhbWVudGUNCg0KYGBge3IsY29sbGFwc2U9VFJVRX0NCmVzdHVkaWFudGVzICU+JSANCiAgc2VsZWN0X2lmKC4sIGlzLm51bWVyaWMpDQoNCmBgYA0KIyBQYXJhIHNlbGVjY2lvbmFyIGxhcyB2YXJpYWJsZXMgcXVlIHNvbiBkZSB0aXBvIGNhcmFjdGVyIHNvbGFtZW50ZQ0KDQpgYGB7cixjb2xsYXBzZT1UUlVFfQ0KZXN0dWRpYW50ZXMgJT4lIA0KICBzZWxlY3RfaWYoLiwgaXMuY2hhcmFjdGVyKQ0KYGBgDQojIyBQb2RlbW9zIHNlbGVjY2lvbmFyIGFsZ3Vub3MgcmVnaXN0cm9zIGNvbiBzbGljZSgpDQoNCmBgYHtyLGNvbGxhcHNlPVRSVUV9DQpiYXNlSU1DICU+JSANCiAgc2xpY2UoMSwgMTE6MTMsIDIxKQ0KDQojLSBOb3MgcXVlZGFtb3MgYWNhDQpgYGANCg0KDQojICoqRWplcmNpY2lvIDEqKg0KDQpFeHBsaWNhIGNvbiB0dXMgcGFsYWJyYXMgbGEgc2VjdWVuY2lhIGFudGVyaW9yLg0KDQpSZXNwdWVzdGE6DQpzbGljZSgxLCAxMToxMywgMjEpOiBzZWxlY2Npb25hIGxhcyBmaWxhcyAxLCBkZWwgMTEgYWwgMTMgeSBsYSAyMS4NCg0KIyAqKkVqZXJjaWNpbyAyICoqDQoNCkNvbiBsYSBiYXNlIGRlIElNQyBxdWUgdmltb3MgYWwgcHJpbmNpcGlvLg0KDQphKSBTZWxlY2Npb25hciBsYXMgdmFyaWFibGVzIFBlc28sIFRhbGxhIGUgSU1DLg0KDQpgYGB7cn0NCnNlbGVjdChiYXNlSU1DLCBQRVNPLCBUQUxMQSwgSU1DKQ0KYGBgDQoNCg0KYikgU2VsZWNjaW9uYXIgbG9zIHJlZ2lzdHJvcyBkZSBsYXMgbXVqZXJlcy4NCg0KYGBge3J9DQpmaWx0ZXIoYmFzZUlNQywgU0VYTz09J0YnKQ0KYGBgDQoNCg0KYykgRGUgbG9zIHJlZ2lzdHJvcyBkZSBsYXMgbXVqZXJlcywgc2VsZWNjaW9uYXIgbGFzIHZhcmlhYmxlcyBFZGFkIHkgQ2F0UGVzby4NCg0KYGBge3J9DQpiYXNlSU1DICU+JQ0KICBmaWx0ZXIoU0VYTyA9PSAnRicpJT4lDQogIHNlbGVjdChTRVhPLEVEQUQsQ2F0UGVzbykNCiAgDQpgYGANCg0KDQpkKSBEZSBsb3MgcmVnaXN0cm9zIGRlIGxhcyBtdWplcmVzLCBzZWxlY2Npb25hciBsYXMgdmFyaWFibGVzIEVkYWQgeSBDYXRwZXNvIHkgbHVlZ28gb3JkZW5hcmxvcyBlbiBmb3JtYSBjcmVjaWVudGUgcG9yIEVkYWQuDQoNCmBgYHtyfQ0KYmFzZUlNQyAlPiUgDQogIGZpbHRlcihTRVhPID09ICdGJykgJT4lIA0KICBzZWxlY3QoU0VYTyxFREFELENhdFBlc28pICU+JSANCiAgYXJyYW5nZShFREFEKQ0KYGBgDQoNCg0KZSkgT3JkZW5hciBsYSBiYXNlIGVuIGZvcm1hIGRlY3JlY2llbnRlIHBvciBJTUMuDQoNCmBgYHtyfQ0KYmFzZUlNQyAlPiUgDQogIGFycmFuZ2UoLUlNQykNCmBgYA0KDQoNCmYpIERlIGxhIGJhc2Ugb3JkZW5hZGEgZW4gZm9ybWEgZGVjcmVjaWVudGUgcG9yIElNQywgc2VsZWNjaW9uYXIgbG9zIHF1ZSB0aWVuZW4gc29icmVwZXNvIHkgcXVlZGFyc2UgY29uIGxhcyBjb2x1bW5hcyBkZSBFZGFkLCBUYWxsYSB5IFBlc28uDQoNCmBgYHtyfQ0KYmFzZUlNQyAlPiUgDQogIGFycmFuZ2UoLUlNQykgJT4lIA0KICBmaWx0ZXIoSU1DID4gMzApICU+JSANCiAgc2VsZWN0KElNQywgRURBRCwgVEFMTEEsIFBFU08pDQpgYGANCg0KDQpnKSBTZWxlY2Npb25hciBsYXMgdmFyaWFibGVzIFRhbGxhLCBJTUMgeSBDQyB5IGZpbHRyYXIgbG9zIHJlZ2lzdHJvcyAxLCA1LCA2LCA3IHkgMTUuIEd1YXJkYXIgZXN0byBlbiB1bmEgYmFzZSBxdWUgc2UgbGxhbWUgYmFzZUlNQ19hdXggeSBtb3N0cmFyIGPDs21vIGVzIGxhIG51ZXZhIGJhc2UuDQoNCg0KYGBge3J9DQpiYXNlSU1DX2F1eCA8LSBiYXNlSU1DICU+JSANCiAgc2VsZWN0KFRBTExBLCBJTUMsIENDKSAlPiUNCiAgc2xpY2UoMSw1OjYsNSkNCg0KYmFzZUlNQ19hdXgNCmBgYA0KDQoNCg0KIyBBw7FhZGlyIG51ZXZhcyB2YXJpYWJsZXM6IG11dGF0ZSgpDQoNClNlZ3VpbW9zIGNvbiBsYXMgYWNjaW9uZXMgYsOhc2ljYXMgaW1wbGVtZW50YWRhcyBlbiBsYSBiaWJsaW90ZWNhIGRwbHlyLg0KDQoqKlZlYW1vcyBjb21vIGNyZWFyIG51ZXZhcyB2YXJpYWJsZXMgcXVlIHNvbiBmdW5jacOzbiBkZSBsYXMgeWEgZXhpc3RlbnRlcy4qKiANCg0KRW4gZXN0ZSBlamVtcGxvIGNyZWFtb3MgdW5hIHZhcmlhYmxlIHF1ZSBjb3JyZXNwb25kZSBhbCBjb2NpZW50ZSBlbnRyZSBsYSBUYWxsYSB5IGxhIENDIChxdWUgcG9kcsOtYSBjb3JyZXNwb25kZXIgYSBhbGfDum4gYXNwZWN0byBkZSBsYSBmb3JtYSBkZSBsYSBwZXJzb25hIGRlIGludGVyw6lzIHBhcmEgc3Ugcmllc2dvIGNhcmRpb3Zhc2N1bGFyKSBlbiBsYSBiYXNlSU1DOg0KDQpgYGB7cixjb2xsYXBzZT1UUlVFfQ0KYmFzZUlNQzwtYmFzZUlNQyAlPiUNCiAgbXV0YXRlKGZvcm1hID0gVEFMTEEvQ0MpDQpiYXNlSU1DDQpgYGANCg0KW09ic2VydmVtb3MgcXVlIGxhIG51ZXZhIHZhcmlhYmxlIGZvcm1hIGFwYXJlY2UgZW4gbGEgw7psdGltYSBjb2x1bW5hLl17c3R5bGU9ImNvbG9yOmRhcmtncmVlbiJ9DQoNCg0KDQpbUG9kcsOtYW1vcyByZWRvbmRlYXIgbG9zIHZhbG9yZXMgZGUgZXN0YSBudWV2YSB2YXJpYWJsZSwgcG9yIGVqZW1wbG8gY29uIHRyZXMgY2lmcmFzIGRlY2ltYWxlcy5de3N0eWxlPSJjb2xvcjpkYXJrZ3JlZW4ifQ0KDQpgYGB7cixjb2xsYXBzZT1UUlVFfQ0KYmFzZUlNQzwtYmFzZUlNQyAlPiUNCiAgbXV0YXRlKGZvcm1hID0gcm91bmQoVEFMTEEvQ0MsMykpDQpiYXNlSU1DDQpgYGANCg0KDQojIFJlc3VtaXIgKHN1YmNvbmp1bnRvcyBkZSkgdmFyaWFibGVzOiBncm91cF9ieSgpICsgc3VtbWFyaXNlKCkNCiMjIFVzYW1vcyBzdW1tYXJpc2UoKSBwYXJhIGFwbGljYXIgY29tYW5kb3MgYSB2YXJpYWJsZXMuIA0KDQoqKk5vcm1hbG1lbnRlIHNlIHVzYSBlbiBjb21iaW5hY2nDs24gY29uIGdyb3VwX2J5KCkgZGUgbWFuZXJhIHF1ZSBzZSBjYWxjdWxlbiANCiBlc3RhZMOtc3RpY29zIHBhcmEgc3ViZ3J1cG9zIGRlIG9ic2VydmFjaW9uZXMuICoqDQogDQogRW4gZWwgc2lndWllbnRlIGVqZW1wbG8gc2UgY2FsY3VsYQ0KIGxhIG1lZGlhIGRlbCBJTUMgcGFyYSBjYWRhIGNhdGVnb3LDrWEgZGUgUGVzbzogDQogDQpgYGB7cixjb2xsYXBzZT1UUlVFfQ0KcmVzdW1lbjE8LWJhc2VJTUMgJT4lDQogICAgZ3JvdXBfYnkoQ2F0UGVzbykgJT4lIHN1bW1hcmlzZShNZWRpYV9mb3JtYT1tZWFuKGZvcm1hKSxuPW4oKSxzdGRldj1zZChmb3JtYSkpICU+JSBtdXRhdGVfaWYoaXMubnVtZXJpYywgfnJvdW5kKC4sIDQpKQ0KcmVzdW1lbjENCg0KYGBgDQoNCg0KIyBQYXJhIGV4dHJhZXIgYWxlYXRvcmlhbWVudGUgYWxndW5hcyBvYnNlcnZhY2lvbmVzIHNhbXBsZV9uKCkNCiAgDQoqRXh0cmFlIDQgb2JzLiBzaW4gcmVlbXBsYXpvIGRlIGxhIGJhc2UgZGUgZXN0dWRpYW50ZXMqDQoNCmBgYHtyLGNvbGxhcHNlPVRSVUV9DQojIFBvZGVtb3MgZmlqYXIgdW5hIHNlbWlsbGEgcXVlIG5vcyBoYWNlIHJlcGV0aWJsZSBhbCBleHBlcmltZW50bw0Kc2V0LnNlZWQoMjcpDQplc3R1ZGlhbnRlcyAlPiUgc2FtcGxlX24oNCkNCmBgYA0KIyBFeHRyYWUgdW4gMjUlIGRlIG9icyBjb24vc2luIHJlZW1wbGF6YW1pZW50bw0KYGBge3IsY29sbGFwc2U9VFJVRX0NCnNldC5zZWVkKDEyMykNCmVzdHVkaWFudGVzICU+JSBzYW1wbGVfZnJhYygwLjI1LCByZXA9RkFMU0UpICU+JSBhcnJhbmdlKE9ic2VydmFjaW9uKSANCiMgZW4gZXN0ZSBjYXNvIHBvZGVtb3Mgb3B0YXIgcG9yIGNvbiBvIHNpbiByZXBvc2ljacOzbiBlbiBlbCBtdWVzdHJlbw0KYGBgDQojIEhheSBkaXN0aW5hcyBwb3NpYmlsaWRhZGVzIHF1ZSBub3MgcGVybWl0ZW4gIHZpc3VhbGl6YXIgbGFzIHZhcmlhYmxlcyBzdSB0aXBvIHkgYWxndW5vcyB2YWxvcmVzLCB2ZWFtb3Mgc2ltaWxpdHVkZXMgeSBkaWZlcmVuY2lhcw0KDQpgYGB7cixjb2xsYXBzZT1UUlVFfQ0KIGdsaW1wc2UoYmFzZUlNQykNCmBgYA0KDQpgYGB7cixjb2xsYXBzZT1UUlVFfQ0Kc3VtbWFyeShiYXNlSU1DKQ0KYGBgDQpgYGB7cixjb2xsYXBzZT1UUlVFfQ0Kc3RyKGJhc2VJTUMpDQpgYGANCg0KDQogT3Ryb3MgcmVzw7ptZW5lcyBjb21vIGRlc3bDrW8gc3RhbmRhcmQgeSBjYW50aWRhZCBkZSByZWdpc3Ryb3MNCj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KYGBge3IsY29sbGFwc2U9VFJVRX0NCmJhc2VJTUMgJT4lIA0KICBzZWxlY3QoVEFMTEEsU0VYTykgJT4lIA0KICBncm91cF9ieShTRVhPKSAlPiUgDQogIHN1bW1hcmlzZShUb3RhbF9SZWdpc3Ryb3M9bigpLCANCiAgICBtZWRpYV90YWxsYT1tZWFuKFRBTExBKSwgDQogICAgc2RfdGFsbGE9c2QoVEFMTEEpKSAlPiUgDQogIGFycmFuZ2UoLW1lZGlhX3RhbGxhKQ0KYGBgDQoNCg0KDQpbUGFyYSBxdWUgbXVlc3RyZSBhbGd1bm9zIHJlZ2lzdHJvcyBkZSBsb3MgcHJpbWVyb3MgdXNhbW9zIGhlYWQoKV17c3R5bGU9ImNvbG9yOmJsdWUifQ0KW1BhcmEgcXVlIG11ZXN0cmUgYWxndW5vcyByZWdpc3Ryb3MgZGUgbG9zIMO6bHRpbW9zIHVzYW1vcyB0YWlsKCkuXXtzdHlsZT0iY29sb3I6Ymx1ZSJ9DQoNCmBgYHtyLGNvbGxhcHNlPVRSVUV9DQplc3R1ZGlhbnRlcyAlPiUgYXJyYW5nZShkZXNjKEZpbmFsKSkgJT4lIGhlYWQoOCkNCmVzdHVkaWFudGVzICU+JSBhcnJhbmdlKEZpbmFsKSAlPiUgdGFpbCg4KQ0KYGBgDQoNCiMgUGFyYSBhZ3JlZ2FyIHVuYSBudWV2YSBmaWxhIGEgbGEgYmFzZSBkZSBkYXRvcyBwb2RlbW9zIHV0aWxpemFyIGFkZF9yb3coKToNCg0KYGBge3IsY29sbGFwc2U9VFJVRX0NCmJhc2VJTUMxIDwtIGJhc2VJTUMgJT4lIGFkZF9yb3coUEFDSUVOVEU9IDE1MSwgRURBRCA9IDEyLCBTRVhPPSJNIiwgIFRBTExBPTEuMyxJTUM9MTYuNyxDQz03NSkNCmJhc2VJTUMxDQpgYGANClsqKk9ic2VydmVuIHF1w6kgb2N1cnJpw7MgY29uIGxhcyB2YXJpYWJsZXMgcXVlIG5vIGNhcmdhbW9zISEqKl17c3R5bGU9ImNvbG9yOmRhcmtibHVlIn0NCg0KDQpPdHJhIGNhcmFjdGVyw61zdGljYSBpbXBvcnRhbnRlIGVzIGVsIGNvbnRlbywgcG9kcsOtYW1vcyBlc3RhciBpbnRlcmVzYWRvcywgZGFkbyB1biBjb25qdW50byBkZSBjbGFzZXMgY3VhbCBlcyBzdSBmcmVjdWVuY2lhIGFic29sdXRhIGRlIGNhZGEgY2F0ZWdvcsOtYSwgcGFyYSBlc3RvIHV0aWxpemFyZW1vcyBjb3VudCgpIGVsIGN1YWwgZ2VuZXJhIHVuYSB2YXJpYWJsZSBxdWUgY29udGllbmUgZGljaGFzIGZyZWN1ZW5jaWFzLg0KDQoNCkFob3JhIGVsaW1pbmFtb3MgbGEvcyBmaWxhL3MgcXVlIHRlbmdhbiBhbGfDum4gcmVnaXN0cm8gZmFsdGFudGUNCmBgYHtyLGNvbGxhcHNlPVRSVUV9DQpiYXNlSU1DMSAlPiUgbmEub21pdCgpDQpgYGANCmBgYHtyLGNvbGxhcHNlPVRSVUV9DQpiYXNlSU1DICU+JSBjb3VudChDYXRQZXNvKQ0KDQoNCiN0YWJsZShiYXNlSU1DJENhdFBlc28pDQpgYGANCg0KYGBge3J9DQpiYXNlSU1DJT4lc3VtbWFyaXplKG5fZGlzdGluY3QoQ2F0UGVzbykpDQoNCiNsZW5ndGgodW5pcXVlKGJhc2VJTUMkQ2F0UGVzbykpDQpgYGANCg0KDQojIFBvZGVtb3MgdXRpbGl6YXIgZWwgY29uZGljaW9uYWwgaWZlbHNlKCkgcGFyYSBjcmVhciBudWV2YXMgIHZhcmlhYmxlcyBzZWfDum4gY2llcnRhcyBjb25kaWNpb25lczoNCg0KYGBge3IsY29sbGFwc2U9VFJVRX0NCmJhc2VJTUMyPC1iYXNlSU1DICU+JSBtdXRhdGUoY2F0ZWdvcsOtYSA9IGlmZWxzZShFREFEID4gMTAsICJNYXlvciIsICJNZW5vciIpKQ0KYmFzZUlNQzINCg0KYGBgDQoNCg0KIyMgUmVub21icmFuZG8gdmFyaWFibGVzDQoNClRhbWJpw6luIHBvZGVtb3MgY2FtYmlhciBlbCBub21icmUgYSB1bmEgdmFyaWFibGUuDQoNCg0KDQpgYGB7cixjb2xsYXBzZT1UUlVFfQ0KZXN0dWRpYW50ZXMgJT4lIA0KICByZW5hbWUoR2FzdG9fdG90YWwgPSBHYXN0b3MpDQoNCg0KIy0gVGFtYmllbiB0ZW5lbW9zIGVzdGFzIG9wY2lvbmVzDQojbmFtZXMoZGYpW25hbWVzKGRmKSA9PSAnb2xkLnZhci5uYW1lJ10gPC0gJ25ldy52YXIubmFtZScNCiNjb2xuYW1lcyhkZilbcG9zaXRpb25dIDwtICJuZXduYW1lMiINCg0KYGBgDQoNCg0KIyBDdWFuZG8gcXVpZXJvIGNhbWJpYXIgZWwgbm9tYnJlIGRlIHRvZGFzIGxhcyBjb2x1bW5hcyBzZXROYW1lcygpDQpgYGB7cixjb2xsYXBzZT1UUlVFfQ0KYmFzZUlNQzM8LWJhc2VJTUMgJT4lIA0KICBzZXROYW1lcyhjKCJQYWNpZW50ZSIsICJFZGFkIiwgIlNleG8iLCJQZXNvIiwiVGFsbGEiLCJCTUkiLCJCTUlfcGVyYyIsIlBDIiwiQ2F0X1Blc28iLCJGb3JtYSIpKQ0KYmFzZUlNQzMNCmBgYA0KDQojICoqRWplcmNpY2lvIDMqKg0KDQpDb24gbG9zIGRhdG9zIGRlIEFkbWlzacOzbiwgY3V5YXMgdmFyaWFibGVzIGluZGljYW4gc2kgZWwgZXN0dWRpYW50ZSBmdWUgYWRtaXRpZG8gKGNhdGVnb3LDrWEgMSkgZW4gdW5hIGNhcnJlcmEgZGUgcG9zZ3JhZG8sIGVsIHB1bnRhamUgZGVsIGVzdHVkaWFudGVzIGVuICBHUkUgKGdyYWR1YXRlIHJlY29yZCBleGFtIHNjb3Jlcy1wdW50YWplcyBkZSBleMOhbWVuZXMgZGUgcmVnaXN0cm8gZGUgcG9zZ3JhZG8pIHkgR1BBIChncmFkZSBwb2ludCBhdmVyYWdlLSBwcm9tZWRpbyBkZSBjYWxpZmljYWNpb25lcyBkZSBsYSBjYXJyZXJhIGRlIGdyYWRvKSB5IGVsIHJhbmsocmFuayBvZiB0aGUgdW5kZXJncmFkdWF0ZSBzY2hvb2wtIHJhbmdvIGRlIGxhIGVzY3VlbGEgZGUgcHJlZ3JhZG8pLg0KDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0UsaW5jbHVkZT1GQUxTRSxjb2xsYXBzZT1UUlVFfQ0KdXJsID0gJ2h0dHA6Ly9zdGF0cy5pZHJlLnVjbGEuZWR1L3N0YXQvc3RhdGEvZGFlL2JpbmFyeS5kdGEnDQp0bXBmaWxlIDwtIHRlbXBmaWxlKCkNCmRvd25sb2FkLmZpbGUodXJsLCB0bXBmaWxlKQ0KYGBgDQoNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRSxjYWNoZT1GQUxTRSxjb2xsYXBzZT1UUlVFfQ0KYWRtaXNpb24gPC0gcmVhZC5kdGEodXJsKSAgDQpoZWFkKGFkbWlzaW9uLDgpDQphZG1pc2lvbg0KYGBgDQoNCmEpIENyZWFyIHVuYSBudWV2YSB2YXJpYWJsZSBxdWUgcHJvbWVkaWUgbG9zIHZhbG9yZXMgZGUgZ3JlIHkgZ3BhLg0KYikgQWdydXBhciBwb3IgcmFuayB5IHByb21lZGlhciBncmUgeSBncGEgcGFyYSBjYWRhIHJhbmsuDQpjKSBFeHRyYWVyIHVuYSBtdWVzdHJhIGRlIDEwMCByZWdpc3Ryb3Mgc2luIHJlcG9zaWNpw7NuIGRlIGRvcyBtYW5lcmFzIGRpc3RpbnRhcy4NCmQpIFJlc3VtaXIgbGFzIHZhcmlhYmxlcyBkZSBsYSBiYXNlDQplKSBBZ3JlZ2FyIHVuYSBmaWxhIHF1ZSByZXN1bHRlIG11eSBkaWZlcmVudGUgYSBsYXMgb3RyYXMgeSBubyBjb2xvY2FyIHJhbmsuDQpmKSBFbGltaW5hciBsb3MgcmVnaXN0cm9zIHF1ZSB0ZW5nYW4gYWxnw7puIHZhbG9yIGZhbHRhbnRlLg0KZykgQ29udGFyIGN1w6FudG9zIGZ1ZXJvbiBhZG1pdGlkb3MgeSBjdWFudG9zIG5vLg0KaCkgQ29udGFyIGN1w6FudG9zIGhheSBkZSBjYWRhIHJhbmdvIGRlIGVzY3VlbGEuDQppKSBDYW1iaWFyIGVsIG5vbWJyZSBkZSB1bmEgbGFzIHZhcmlhYmxlcyAoc2VhbiBjcmVhdGl2b3MhKS4NCmopIENhbWJpYXIgZWwgbm9tYnJlIGRlIHRvZGFzIGxhcyB2YXJpYWJsZXMgKHDDs25nYW5sZSBodW1vciEpLg0KDQoNCg0KDQojIENhbWJpYXIgbml2ZWxlcyBkZSB1biBmYWN0b3INCg0KRW4gbGEgYmFzZSBkZSBlc3R1ZGlhbnRlcyBxdWVyZW1vcyByZWNvZGlmaWNhciBsYSB2YXJpYWJsZSBGdW1hDQoNCmBgYHtyLGNvbGxhcHNlPVRSVUV9DQpkZiA8LSBlc3R1ZGlhbnRlc1sxOjQwLDE6MTBdDQpkZjwtIGRmICU+JSBtdXRhdGVfYXQoIkZ1bWEiLCBmYWN0b3IpDQpsZXZlbHMoZGYkRnVtYSlbMV0gPC0gIjAiDQpsZXZlbHMoZGYkRnVtYSlbMl0gPC0gIjEiDQpkZg0KYGBgDQoNCg0KIyBQcmVzZW50YWNpw7NuIGRlIFRhYmxhcyBlIEluZm9ybWVzIGJvbml0b3MNCg0KVmFtb3MgYSB2ZXIgZGlzdGludGFzIGZvcm1hcyBkZSBwcmVzZW50YXIgdW5hIHNhbGlkYSBkZSB1bmEgdGFibGEgcmVzdW1lbiBtZWpvcmFuZG8gZWwgYXNwZWN0byBiw6FzaWNvLg0KDQpgYGB7cixjb2xsYXBzZT1UUlVFfQ0KcmVzdW1lbjElPiUNCiAga2JsKCkgJT4la2FibGVfbWF0ZXJpYWwoZnVsbF93aWR0aD1GQUxTRSkNCmBgYA0KDQpgYGB7cixjb2xsYXBzZT1UUlVFfQ0KcmVzdW1lbjEgJT4lDQogIGtibCgpICU+JQ0KICBrYWJsZV9wYXBlcigiaG92ZXIiLCBmdWxsX3dpZHRoID0gRikNCmBgYA0KDQpgYGB7cixjb2xsYXBzZT1UUlVFfQ0KcmVzdW1lbjEgJT4lDQprYmwoY2FwdGlvbiA9ICJIYWNlbW9zIHVuYSBUYWJsYSBkZSBNZWpvciBFc3RpbG8iKSAlPiUNCiAga2FibGVfY2xhc3NpYyhmdWxsX3dpZHRoID0gRiwgaHRtbF9mb250ID0gIkNhbWJyaWEiKQ0KYGBgDQoNCmBgYHtyLGNvbGxhcHNlPVRSVUV9DQpyZXN1bWVuMSAlPiUNCiAga2JsKCkgJT4lDQogIGthYmxlX2NsYXNzaWNfMihmdWxsX3dpZHRoID0gRikNCmBgYA0KDQojIEVzdGlsbyBPc2N1cm8NCmBgYHtyLGNvbGxhcHNlPVRSVUV9DQpyZXN1bWVuMSAlPiUNCiAga2JsKCkgJT4lDQogIGthYmxlX21hdGVyaWFsX2RhcmsoKQ0KYGBgDQoqKnRpZHlyKioNCg0KPHNwYW4gc3R5bGU9ImNvbG9yOiMyOThBMDgiPmdhdGhlcigpPC9zcGFuPg0KDQo8c3BhbiBzdHlsZT0iY29sb3I6IzI5OEEwOCI+c3ByZWFkKCk8L3NwYW4+DQoNCjxzcGFuIHN0eWxlPSJjb2xvcjojMjk4QTA4Ij5zZXBhcmF0ZSgpPC9zcGFuPg0KDQo8c3BhbiBzdHlsZT0iY29sb3I6IzI5OEEwOCI+dW5pdGUoKTwvc3Bhbj4NCg0KKipkcGx5cioqDQoNCjxzcGFuIHN0eWxlPSJjb2xvcjojREYzQTAxIj5zZWxlY3QoKTwvc3Bhbj4NCg0KPHNwYW4gc3R5bGU9ImNvbG9yOiNERjNBMDEiPmZpbHRlcigpPC9zcGFuPg0KDQo8c3BhbiBzdHlsZT0iY29sb3I6I0RGM0EwMSI+Z3JvdXBfYnkoKTwvc3Bhbj4NCg0KPHNwYW4gc3R5bGU9ImNvbG9yOiNERjNBMDEiPnN1bW1hcmlzZSgpPC9zcGFuPg0KDQo8c3BhbiBzdHlsZT0iY29sb3I6I0RGM0EwMSI+YXJyYW5nZSgpPC9zcGFuPg0KDQo8c3BhbiBzdHlsZT0iY29sb3I6I0RGM0EwMSI+am9pbigpPC9zcGFuPg0KDQo8c3BhbiBzdHlsZT0iY29sb3I6I0RGM0EwMSI+bXV0YXRlKCk8L3NwYW4+DQoNCg0KRWwgYXJjaGl2byBmbGlnaHRzIGVzdMOhIGRpc3BvbmlibGUgZW4gUiB5IGNvbnRpZW5lIGluZm9ybWFjacOzbiByZWxhdGl2YSBhIHNhbGlkYXMgZGUgdnVlbG9zIGRlIE5ZQyBlbiAyMDEzDQoNCmBgYHtyLGNvbGxhcHNlPVRSVUV9DQpmbGlnaHRzICU+JQ0KICAgc2VsZWN0KHllYXIsIG1vbnRoLCBkYXksIGhvdXIsIG1pbnV0ZSkNCmBgYA0KIyBBcm1hbW9zIHVuYSBmZWNoYSBhIHBhcnRpciBkZSB0cmVzIHZhcmlhYmxlcw0KYGBge3IsY29sbGFwc2U9VFJVRX0NCmZsaWdodHMgJT4lDQogIHNlbGVjdCh5ZWFyLCBtb250aCwgZGF5LCBob3VyLCBtaW51dGUpICU+JQ0KICAgIG11dGF0ZShob3JhX3BhcnRpZGEgPSBtYWtlX2RhdGV0aW1lKHllYXIsIG1vbnRoLCBkYXksaG91cixtaW51dGUpDQogICkNCmBgYA0KIyBGdXNpb25hIHRyZXMgY29sdW1uYXM6IHllYXIsIG1vbnRoIHkgZGF5IGVuIHVuYSBudWV2YSBjb2x1bW5hICJmZWNoYSIuDQpgYGB7cixjb2xsYXBzZT1UUlVFfQ0KDQp2dWVsb3M8LWZsaWdodHMgJT4lIHVuaXRlKCJmZWNoYSIsIHllYXIsbW9udGgsIGRheSwgc2VwID0gIi8iKQ0KdnVlbG9zDQpgYGANCiMgTGEgZnVuY2nDs24gc2VwYXJhdGUoKSBzZXBhcmEgZW4gZG9zIGNvbHVtbmFzICBsYSBmZWNoYQ0KDQpgYGB7cix3YXJuaW5nPUZBTFNFLGNhY2hlPUZBTFNFLG1lc3NhZ2U9RkFMU0UsY29sbGFwc2U9VFJVRX0NCnZ1ZWxvcyAlPiUNCiAgc2VwYXJhdGUoZmVjaGEsIGludG8gPSBjKCJhw7FvIiwgIm1lcyIpKQ0KDQp2dWVsb3MgJT4lDQogIHNlcGFyYXRlKGZlY2hhLCBpbnRvID0gYygiYcOxbyIsICJtZXMiKSklPiUNCiB1bml0ZSgiY2FtcG8iLGHDsW8sbWVzLHNlcD0iLSIpDQpgYGANCiMgVmFtb3MgYSB2ZXIgY29tbyBkYXJsZSBudWV2YSBmb3JtYSBhIHVuYSBiYXNlIGRlIGRhdG9zIHNlZ8O6biBlbCBhbsOhbGlzaXMgcXVlIHF1ZXJyYW1vcyBpbXBsZW1lbnRhciBwdWVkZSBzZXIgbmVjZXNhcmlvDQoNCg0KYGBge3IsY29sbGFwc2U9VFJVRX0NCiNDYXJnYW1vcyBkb3MgYmFzZXMgZGUgZGF0b3M6IGxhIHByaW1lcmEgdGllbmUgZWwgbm9tYnJlIHkgZWwgYXBlbGxpZG8gZGUgbG9zIG3DunNpY29zIGNvbmp1bnRhbWVudGUgY29uIGVsIA0KI2luc3RydW1lbnRvIHF1ZSBlamVjdXRhbiBwcmluY2lwYWxtZW50ZS4NCg0KYXJ0aXN0c0tIIDwtIHRpYmJsZShmaXJzdCA9IGMoIkppbW15IiwgIkdlb3JnZSIsICJNaWNrIiwgIlRvbSIsICJEYXZ5IiwgIkpvaG4iLCAiUGF1bCIsICJKaW1teSIsICJKb2UiLCAiRWx2aXMiLCAiS2VpdGgiLCAiUGF1bCIsICJSaW5nbyIsICJKb2UiLCAiQnJpYW4iLCAiTmFuY3kiKSwgbGFzdCA9IGMoIkJ1ZmZldHQiLCAiSGFycmlzb24iLCAiSmFnZ2VyIiwgIkpvbmVzIiwgIkpvbmVzIiwgIkxlbm5vbiIsICJNY0NhcnRuZXkiLCAiUGFnZSIsICJQZXJyeSIsICJQcmVzbGV5IiwgIlJpY2hhcmRzIiwgIlNpbW9uIiwgIlN0YXJyIiwgIldhbHNoIiwgIldpbHNvbiIsICJXaWxzb24iKSwgaW5zdHJ1bWVudCA9IGMoIkd1aXRhciIsICJHdWl0YXIiLCAiVm9jYWxzIiwgIlZvY2FscyIsICJWb2NhbHMiLCAiR3VpdGFyIiwgIkJhc3MiLCAiR3VpdGFyIiwgIkd1aXRhciIsICJWb2NhbHMiLCAiR3VpdGFyIiwgIkd1aXRhciIsICJEcnVtcyIsICJHdWl0YXIiLCAiVm9jYWxzIiwgIlZvY2FscyIpKQ0KYXJ0aXN0cyA8LSBhcnRpc3RzS0gNCmFydGlzdHMlPiVoZWFkKCkNCg0KYGBgDQpgYGB7cixjb2xsYXBzZT1UUlVFfQ0KIyBMYSBzZWd1bmRhIGJhc2UgdGllbmUgZWwgbm9tYnJlIHkgZWwgYXBlbGxpZG8gZGUgbG9zIGFydGlzdGFzIGNvbmp1bnRhbWVudGUgY29uIGxhIGJhbmRhIGRvbmRlIHNlIGhpY2llcm9uIGZhbW9zb3MuDQoNCmJhbmRzS0ggPC0gdGliYmxlKGZpcnN0ID0gYygiSm9obiIsICJKb2huIFBhdWwiLCAiSmltbXkiLCAiUm9iZXJ0IiwgIkdlb3JnZSIsICJKb2huIiwgIlBhdWwiLCAiUmluZ28iLCAiSmltbXkiLCAiTWljayIsICJLZWl0aCIsICJDaGFybGllIiwgIlJvbm5pZSIpLCBsYXN0ID0gYygiQm9uaGFtIiwgIkpvbmVzIiwgIlBhZ2UiLCAiUGxhbnQiLCAiSGFycmlzb24iLCAiTGVubm9uIiwgIk1jQ2FydG5leSIsICJTdGFyciIsICJCdWZmZXR0IiwgIkphZ2dlciIsICJSaWNoYXJkcyIsICJXYXR0cyIsICJXb29kIiksIGJhbmQgPSBjKCJMZWQgWmVwcGVsaW4iLCAiTGVkIFplcHBlbGluIiwgIkxlZCBaZXBwZWxpbiIsICJMZWQgWmVwcGVsaW4iLCAiVGhlIEJlYXRsZXMiLCAiVGhlIEJlYXRsZXMiLCAiVGhlIEJlYXRsZXMiLCAiVGhlIEJlYXRsZXMiLCAiVGhlIENvcmFsIFJlZWZlcnMiLCAiVGhlIFJvbGxpbmcgU3RvbmVzIiwgIlRoZSBSb2xsaW5nIFN0b25lcyIsICJUaGUgUm9sbGluZyBTdG9uZXMiLCAiVGhlIFJvbGxpbmcgU3RvbmVzIikpDQpiYW5kc0tIJT4laGVhZCgpDQpgYGANCg0KIyBKdW50YW1vcyBkb3MgYmFzZXMgdXRpbGl6YW5kbyB1bmEgbyBkb3MgdmFyaWFibGVzIGVuIGNvbcO6biBjb24gbGVmdF9qb2luKCkNCg0KYGBge3IsY29sbGFwc2U9VFJVRX0NCmJhbmRzMiA8LSBsZWZ0X2pvaW4oYmFuZHNLSCwgYXJ0aXN0cywgYnkgPSBjKCJmaXJzdCIsICJsYXN0IikpDQpiYW5kczIgJT4lIGhlYWQoKQ0KYGBgDQoNCg0KDQojICoqPHNwYW4gc3R5bGU9ImNvbG9yOiNERjNBMDEiPiBPYnNlcnZlbiBRdcOpIGRpZmVyZW5jaWEgaGF5IGNvbiByaWdodF9qb2luKCkgPC9zcGFuPioqDQoNCg0KYGBge3IsY29sbGFwc2U9VFJVRX0NCg0KYmFuZHMzIDwtIHJpZ2h0X2pvaW4oYXJ0aXN0cywgYmFuZHNLSCwgYnkgPSBjKCJmaXJzdCIsICJsYXN0IikpDQpiYW5kczMgJT4laGVhZCgpDQphcnRpc3RzDQpiYW5kc0tIDQpgYGANCmBgYHtyLGNvbGxhcHNlPVRSVUV9DQojIGlubmVyX2pvaW4gc29sbyByZXRpZW5lIGxhcyBmaWxhcyBxdWUgZXN0YW4gZW4gYW1iYXMgYmFzZXMNCmJhbmRzNCA8LSBpbm5lcl9qb2luKGJhbmRzS0gsIGFydGlzdHMsIGJ5ID0gYygiZmlyc3QiLCAibGFzdCIpKQ0KYmFuZHM0IA0KYGBgDQoNCg0KYGBge3IsY29sbGFwc2U9VFJVRX0NCiMgZnVsbF9qb2luIHJldGllbmUgbGFzIGZpbGFzIGRlIGN1YWxxdWllcmEgZGUgbGFzIGJhc2VzIGF1bnF1ZSBubyBlc3TDqW4gZW4gbGEgb3RyYQ0KDQpiYW5kczUgPC0gZnVsbF9qb2luKGJhbmRzS0gsIGFydGlzdHMsIGJ5ID0gYygiZmlyc3QiLCAibGFzdCIpKQ0KYmFuZHM1DQpgYGANCg0KDQoNCiMgUGFzYW5kbyBkZSBmb3JtYXRvIGFuY2hvIGEgZm9ybWF0byBsYXJnbyB5IHZpY2V2ZXJzYQ0KDQpgYGB7cixjb2xsYXBzZT1UUlVFfQ0KIyBFbiBlc3RhIHByaW1lcmEgYmFzZSBoYXkgdHJlcyBjb2x1bW5hcyBwYXJhIGNhZGEgZW1wbGVhZG8gZW4gbGFzIGNvbHVtbmFzIGVzdMOhbiBsb3Mgc2FsYXJpb3MgbWVkaW9zIGRlIHRyZXMgYcOxb3MgZGUgdHJhYmFqbw0KZGF0YV8yIDwtIGRhdGEuZnJhbWUoIm5hbWVzIiA9IGMoIlBlZHJvIiwgIkNhcmxhIiwgIk1hcnRhIiksIA0KICAgICAgICAgICAgICAgICAgICAgICJXXzIwMTQiID0gYygxMDAsIDQwMCwgMjAwKSwgDQogICAgICAgICAgICAgICAgICAgICAgIldfMjAxNSIgPSBjKDUwMCwgNjAwLCA3MDApLA0KICAgICAgICAgICAgICAgICAgICAgICJXXzIwMTYiID0gYygyMDAsIDI1MCwgOTAwKSApDQoNCmRhdGFfMg0KYGBgDQoNCg0KYGBge3IsY29sbGFwc2U9VFJVRX0NCiMgRW4gZXN0YSBzZWd1bmRhIGJhc2UgZXN0YW4gbG9zIG1pc21vcyBkYXRvcyBwZXJvIGFob3JhIHBhcmEgY2FkYSBhw7FvIHkgcGFyYSBjYWRhIGVtcGxlYWRvIGhheSB1biByZWdpc3RybyBkb25kZSBlc3RhIHN1IHNhbGFyaW8NCmRhdGFfMyA8LSBkYXRhLmZyYW1lKG5hbWVzID0gYygiUGVkcm8iLCAiQ2FybGEiLCAiTWFydGEiLCAiUGVkcm8iLCAiQ2FybGEiLCAiTWFydGEiLCAiUGVkcm8iLCAiQ2FybGEiLCAiTWFydGEiKSwgIHllYXIgPSBjKCIyMDE0IiwgIjIwMTQiLCAiMjAxNCIsICAiMjAxNSIsICIyMDE1IiwgIjIwMTUiLCAgIjIwMTYiLCAiMjAxNiIsICIyMDE2IiksIHNhbGFyaW8gPSBjKDEwMCwgNDAwLCAyMDAsIDUwMCwgNjAwLCA3MDAsIDIwMCwgMjUwLDkwMCkpDQoNCmRhdGFfMw0KDQpgYGANCiMgTGEgZnVuY2nDs20gZ2F0aGVyKCkgdHJhbnNmb3JtYSBsb3MgZGF0b3MgZGUgZm9ybWF0byBhbmNobyh3aWRlKSBhIGZvcm1hdG8gbGFyZ28obG9uZykNCg0KYGBge3IsY29sbGFwc2U9VFJVRX0NCmRhdGFfd2lkZSA8LSBkYXRhXzIgICAjLSBkYXRhXzIgZXN0w6EgZW4gZm9ybWF0byBhbmNobyAod2lkZSkNCg0KZGF0YV9sb25nIDwtIGRhdGFfd2lkZSAlPiUgZ2F0aGVyKHBlcmlvZG8sIHNhbGFyaW8sIDI6NCkgIyAyOjQgc29uIGxhcyBjb2x1bW5hcyBxdWUgdmEgYSBwYXNhciBhbCBvdHJvIGZvcm1hdG8NCiMgcGVyaW9kbyB5IHNhbGFyaW8gc29uIGxvcyBudWV2b3Mgbm9tYnJlcyBkZSBlc2FzIGNvbHVtbmFzDQpkYXRhX2xvbmcNCmBgYA0KIyMgU2kgbWUgbW9sZXN0YSBXXyB5IGxvIHF1aWVybyBzYWNhci4uLnB1ZWRvISENCg0KYGBge3IsY29sbGFwc2U9VFJVRX0NCg0KZGF0YV9sb25nIDwtIGRhdGFfbG9uZyAlPiUgbXV0YXRlKHBlcmlvZG8gPSBzdHJfcmVwbGFjZShwZXJpb2RvLCAiV18iLCAiIiApKSMgbGUgaW5kaWNvIHF1ZSBlbiBsYSBjb2x1bW5hIHBlcmlvZG8gcmVlbXBsYWNlIHdfIHBvciBuYWRhDQpkYXRhX2xvbmcNCmRhdGFfbG9uZyRwZXJpb2RvIDwtIGdzdWIoIldfIiwgIi4iLCBkYXRhX2xvbmckcGVyaW9kbykNCg0KYGBgDQoNCiMjIFBhc2FyIHBhc2FyIGRlIGZvcm1hdG8gbGFyZ28gYSBmb3JtYXRvIGFuY2hvLCB0aWR5ciB0aWVuZSBsYSBmdW5jacOzbiBzcHJlYWQoKQ0KDQpgYGB7cixjb2xsYXBzZT1UUlVFfQ0KZGF0YV93aWRlMiA8LSBkYXRhX2xvbmcgJT4lIHNwcmVhZChwZXJpb2RvLCBzYWxhcmlvKQ0KZGF0YV93aWRlMg0KDQpgYGANCiMgKipFamVyY2NpbyA0KioNCg0KQ29uIGxhcyBiYXNlcyBkZSBkYXRvcyBDbGllbnRlcy54bHN4IHkgUmVnaXN0cm9zLnhsc3ggc2UgcGlkZToNCg0KYSkgU2VwYXJhciBsYSB2YXJpYWJsZSBDbGllbnRlIGVuIGRvcyB2YXJpYWJsZXM6IEFwZWxsaWRvIHkgTm9tYnJlLg0KYikgVW5pciBhbWJhcyB2YXJpYWJsZXMgZW4gdW5hIHNvbGEgcXVlIHNlIGxsYW1lIElEIHkgc2VwYXJhciBhcGVsbGlkbyB5IG5vbWJyZSBjb24gIi0iLg0KYykgVW5pciBsYXMgYmFzZXMgZGUgQ2xpZW50ZXMgeSBSZWdpc3Ryb3MgZGVqYW5kbyBsYXMgZmVjaGFzIGFsIGZpbmFsIHkgZ3VhcmRhcmxvIGVuIHVuYSBudWV2YSBiYXNlLg0KZCkgw41kZW0gcGVybyBkZWphbmRvIGxhIGZlY2hhIGFsIGxhZG8gZGVsIG5vbWJyZSB5IGd1YXJkYXJsbyBlbiB1bmEgbnVldmEgYmFzZS4NCmUpIFNlbGVjY2lvbmFyIGRlIGxhIHVsdGltYSBiYXNlLCBsb3MgcmVnaXN0cm9zIGNvcnJlc3BvbmRpZW50ZXMgYSBQSU1FUyB5IGVsZWdpciBhIGxvcyDDumx0aW1vcyAxMCBxdWUgc2UgYXRlbmRpw7MsIGd1YXJkYXJsbyBlbiB1bmEgdGFibGEgeSBwcmVzZW50YXIgdW5hIHRhYmxhIGJvbml0YSBjb21vIHNhbGlkYQ0KDQoNCg0KDQojICoqRWplcmNpY2lvIDUqKg0KDQpDb24gbG9zIGRhdG9zIGRlIGxhIGJhc2UgY29uc3Vtb19veGlnZW5vLnhsc3gNCmEpIENvbG9jYXIgZW4gdW5hIHZhcmlhYmxlIGVsIGTDrWEgeSBlbiBsYSBvdHJhIGVsIGNvbnN1bW8uIEd1YXJkYXIgZW4gdW5hIG51ZXZhIGJhc2UgZGUgZGF0b3MuDQpiKSBBIGxhIG51ZXZhIGJhc2UgZGUgZGF0b3Mgdm9sdmVybGEgYWwgZm9ybWF0byBhbnRlcmlvci4NCg0KDQoNCg==